没有缓存的 MySQL 增强性能

Posted

技术标签:

【中文标题】没有缓存的 MySQL 增强性能【英文标题】:MySQL Enhancing Performance without Cache 【发布时间】:2011-09-15 14:11:01 【问题描述】:

我正在使用 mysql 5.5.14 版从 500 万行的表中运行以下查询:

SELECT P.ID, P.Type, P.Name, P.cty
     , X(P.latlng) as 'lat', Y(P.latlng) as 'lng'
     , P.cur, P.ak, P.tn, P.St, P.Tm, P.flA, P.ldA, P.flN
     , P.lv, P.bd, P.bt, P.nb
     , P.ak * E.usD as 'usP' 
FROM PIG P 
  INNER JOIN EEL E 
    ON E.cur = P.cur 
WHERE act='1' 
  AND flA >= '1615' 
  AND ldA >= '0' 
  AND yr >= (YEAR(NOW()) - 100) 
  AND lv >= '0' 
  AND bd >= '3' 
  AND bt >= '2' 
  AND nb <= '5' 
  AND cDate >= NOW() 
  AND MBRContains(LineString( Point(-65.6583, -87.8906)
                            , Point(65.6583, 87.8906)
                            ), latlng) 
  AND Type = 'g' 
  AND tn = 'l' 
  AND St + Tm - YEAR(NOW()) >= '30' 
HAVING usP BETWEEN 300/2 AND 300 LIMIT 100;

表定义为:

CREATE TABLE `PIG` (
  `ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `Email` char(50) NOT NULL,
  `Type` char(1) NOT NULL,
  `Name` char(25) DEFAULT NULL,
  `cty` char(2) DEFAULT NULL,
  `latlng` point NOT NULL,
  `tn` char(1) NOT NULL DEFAULT 'l',
  `St` smallint(4) unsigned NOT NULL DEFAULT '0',
  `Tm` smallint(3) unsigned NOT NULL DEFAULT '0',
  `yr` smallint(4) unsigned NOT NULL DEFAULT '0',
  `flA` mediumint(6) unsigned NOT NULL DEFAULT '0',
  `ldA` mediumint(6) unsigned NOT NULL DEFAULT '0',
  `flN` smallint(3) unsigned NOT NULL DEFAULT '1',
  `lv` smallint(3) unsigned NOT NULL DEFAULT '0',
  `bd` tinyint(2) unsigned NOT NULL DEFAULT '0',
  `bt` tinyint(2) unsigned NOT NULL DEFAULT '0',
  `nb` tinyint(1) unsigned NOT NULL DEFAULT '9',
  `cur` char(3) DEFAULT NULL,
  `ak` int(10) unsigned NOT NULL DEFAULT '0',
  `Des` tinytext,
  `pDate` datetime DEFAULT NULL,
  `cDate` date DEFAULT NULL,
  `act` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `bid` tinyint(3) unsigned NOT NULL DEFAULT '0',
  `ab` tinyint(3) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`ID`),
  KEY `id_ca` (`cty`,`ak`),
  SPATIAL KEY `id_latlng` (`latlng`)
) ENGINE=MyISAM AUTO_INCREMENT=5000001 DEFAULT CHARSET=latin1

还有:

CREATE TABLE `EEL` (
  `cur` char(3) NOT NULL,
  `usD` decimal(11,10) NOT NULL,
  PRIMARY KEY (`cur`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

查询执行计划如下:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: P
         type: range
possible_keys: id_latlng
          key: id_latlng
      key_len: 34
          ref: NULL
         rows: 742873
        Extra: Using where
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: E
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 3
          ref: BS.P.cur
         rows: 1
        Extra: 

由于 NOW() 函数的存在,此查询不使用查询缓存。从我之前的posting 中,我发现存在其他形式的缓存可以将查询从 300 秒的初始时间缩短到 2 秒以下。我的问题是:“知道缓存不会有太大用处,因为 latlng 的搜索条件不断变化,如何改进上述查询时间?”请注意,latlng 上的空间索引已经为优化目的而构建。

干杯,本

【问题讨论】:

是否可以将 now() 替换为固定的日期时间,例如作为参数?您可以根据日期时间的精度使用缓存。 使用EXPLAIN ... \G* 以获得更好的可读性;) @Colin,我可以将 NOW() 更改为 CURDATE() 但 CURDATE() 也是不可缓存的。 【参考方案1】:

好的索引是具有高选择性的索引。您的条件主要是范围条件,这对可以在复合索引中使用的字段构成了限制。

要调查的可能索引(由具有相等性检查并在末尾添加的字段组成,其中一个字段具有范围检查):

(act, Type, tn, flA)

(act, Type, tn, cDate)

(act, Type, tn, nb)

要在不创建索引的情况下检查选择性,您可以使用:

SELECT COUNT(*)
FROM PIG P 
WHERE act='1' 
  AND Type = 'g' 
  AND tn = 'l' 
  AND flA >= '1615'

SELECT COUNT(*)
FROM PIG P 
WHERE act='1' 
  AND Type = 'g' 
  AND tn = 'l' 
  AND cDate >= NOW() 

SELECT COUNT(*)
FROM PIG P 
WHERE act='1' 
  AND Type = 'g' 
  AND tn = 'l' 
  AND nb <= '5' 

并将输出与空间索引中的742873 进行比较。

【讨论】:

我之前也考虑过使用多列索引,但我不确定它是否会影响我的插入查询速度。因此,我一次一步地检查哪个单列索引执行得最快。我确实希望具有高基数的空间索引表现更好。但我不希望它如此依赖缓存来提高速度。如果我使用上述多列索引,我会遇到类似的缓存问题吗? 这取决于表的更新频率(行 INSERTed、UPDATEd、DELETEd)。关于插入查询速度,您可以考虑将表拆分为一个包含除空间之外的所有数据的 InnoDB 和一个包含空间数据+索引的 MyISAM。 我明白你的意思。检查的行数越少,查询越快。我会试试的,明天让你知道结果。感谢您在该主题上的耐心和友好帮助。 =) 我已按照您的建议检查了三个案例的选择计数(*)分别为 161341、57324 和 137262。我为 first case 构建了一个四列索引并运行了查询,该查询在第一次运行时产生了 160sec 的查询时间。使用全表扫描,即忽略索引,我可以在第一次运行时获得7.7sec 的查询时间。我相信即使具有更高的选择性,其他两个指标的时间也不会显着减少。我是继续添加五列或六列索引还是要进行全表扫描?

以上是关于没有缓存的 MySQL 增强性能的主要内容,如果未能解决你的问题,请参考以下文章

面试如何回答优化数据库

数据库优化

MySQL 8复制性能的增强

MySQL查询和静态缓存的性能比较

MySQL性能优化

MySQL性能优化(来源于简书)