MySQL查询没有正确使用索引?

Posted

技术标签:

【中文标题】MySQL查询没有正确使用索引?【英文标题】:MySQL query not using index correctly? 【发布时间】:2014-10-08 18:25:11 【问题描述】:

我有 2 个表 - profile 有 1,500,000 条记录,indicator_trades 有 12,000,000 条记录。以下查询返回 0 个结果,大约需要 10 秒才能完成。

SELECT `trd`.`symbol`, `p`.`type`
FROM `indicator_trades` AS `trd`
INNER JOIN `profile` AS `p` ON `p`.`symbol` = `trd`.`symbol`
WHERE `start_date` >= '2014-09-17' AND `p`.`type` = 2

描述结果:

+----+-------------+-------+--------+-----------------------+------------+---------+--------------------------+------+-------------+
| id | select_type | table | type   | possible_keys         | key        | key_len | ref                      | rows | Extra       |
+----+-------------+-------+--------+-----------------------+------------+---------+--------------------------+------+-------------+
|  1 | SIMPLE      | trd   | range  | IDX_symbol,start_date | start_date | 4       | NULL                     | 3662 | Using where |
|  1 | SIMPLE      | p     | eq_ref | IDX_symbol,type       | IDX_symbol | 34      | barchart_data.trd.symbol |    1 | Using where |
+----+-------------+-------+--------+-----------------------+------------+---------+--------------------------+------+-------------+

当这个查询使用键时,10 秒似乎是一个异常长的时间,但是从indicator_trades 表中的 12,000,000 条记录中减少 3662 条记录只需要这么长的时间吗?

在两个表上显示创建:

CREATE TABLE `indicator_trades` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `symbol` varchar(32) NOT NULL,
  `indicator_code` varchar(10) NOT NULL,
  `start_date` date DEFAULT NULL,
  `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `IDX_symbol` (`symbol`) USING BTREE,
  KEY `start_date` (`start_date`),
  KEY `indicator_code` (`indicator_code`)
) ENGINE=InnoDB AUTO_INCREMENT=12582721 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT |

CREATE TABLE `profile` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `symbol` varchar(32) NOT NULL,
  `type` int(11) DEFAULT NULL,
  `lastupdate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`),
  UNIQUE KEY `IDX_symbol` (`symbol`) USING BTREE,
  KEY `type` (`type`),
  KEY `exchange` (`exchange`)
) ENGINE=InnoDB AUTO_INCREMENT=320948248 DEFAULT CHARSET=latin1

【问题讨论】:

它需要加入 1200 万行。我认为是内部连接让它变慢了。 @S.Pols 不应该只加入3662条记录,因为优化器应该在加入前用WHERE减少数据集吗? 这听起来像innodb_buffer_pool_size 变量非常低,你打的是磁盘而不是内存。 SHOW VARIABLES LIKE '%buffer_pool%'; 显示什么? @N.B.它显示的值为 4294967296。 大约 4GB,很可能不足以存储您需要使用的所有大约 1350 万行,这会迫使 mysql 使用您的 HDD。由于您的驱动器是机械的(从 10 秒的性能指标来看),这将使整个操作变慢。解决方案(第一手) - 如果您有足够的 RAM,则增加缓冲池,获得更好的硬盘驱动器(SSD 可以解决问题)。其他解决方案是减少磁盘空间占用 - 如果可能,将 varchar symbol 替换为 int fk,尝试不同的存储引擎 (TokuDB),删除基数较低的索引。 【参考方案1】:

乍一看,您的查询应该很快,EXPLAIN 输出也描述了这一点。

您无法避免磁盘读取,因为您需要获取不在所使用索引中的字段(例如:使用索引start_date,但需要重新读取symbol 字段;使用symbol 索引但读取@ 987654324@ 字段)。

您还需要确保所有索引都加载到内存中(buffer_pool 设置允许您增加它)。

您的查询可以这样重写(我认为应该有相同的性能):

SELECT `p`.`symbol`, 
       2 AS `type` 
FROM   `profile` AS `p` 
WHERE  `p`.`symbol` IN (SELECT DISTINCT( `trd`.`symbol` ) 
                        FROM   `indicator_trades` AS `trd` 
                        WHERE  `trd`.`start_date` >= '2014-09-17') 
       AND `p`.`type` = 2 

但是您可以运行子查询并告诉我们它的速度是多少:

SELECT DISTINCT( `trd`.`symbol` ) 
                            FROM   `indicator_trades` AS `trd` 
                            WHERE  `trd`.`start_date` >= '2014-09-17'

【讨论】:

以上是关于MySQL查询没有正确使用索引?的主要内容,如果未能解决你的问题,请参考以下文章

Mysql 索引-2

MySQL索引类型

mysql索引总结----mysql 索引类型以及创建

MySQL索引使用方法和性能优化

MySQL---正确使用索引limit分页执行计划慢日志查询

MySQL索引使用方法和性能优化