繁体   English   中英

为什么MySQL不使用EXPLAIN中的索引?

[英]Why does MySQL not use the index from EXPLAIN?

我有一个简单的表,目前有约1000万行。 这是定义:

CREATE TABLE `train_run_messages` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `train_id` int(10) unsigned NOT NULL,
  `customer_id` int(10) unsigned NOT NULL,
  `station_id` int(10) unsigned NOT NULL,
  `train_run_id` int(10) unsigned NOT NULL,
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `type` tinyint(4) NOT NULL,
  `customer_station_track_id` int(10) unsigned DEFAULT NULL,
  `lateness_type` tinyint(3) unsigned NOT NULL,
  `lateness_amount` mediumint(9) NOT NULL,
  `lateness_code` tinyint(3) unsigned DEFAULT '0',
  `info_text` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `timestamp` (`timestamp`),
  KEY `lateness_amount` (`lateness_amount`),
  KEY `customer_timestamp` (`customer_id`,`timestamp`),
  KEY `trm_customer` (`customer_id`),
  KEY `trm_train` (`train_id`),
  KEY `trm_station` (`station_id`),
  KEY `trm_trainrun` (`train_run_id`),
  KEY `FI_trm_customer_station_tracks` (`customer_station_track_id`),
  CONSTRAINT `FK_trm_customer_station_tracks` FOREIGN KEY (`customer_station_track_id`) REFERENCES `customer_station_tracks` (`id`),
  CONSTRAINT `trm_customer` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `trm_station` FOREIGN KEY (`station_id`) REFERENCES `stations` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `trm_train` FOREIGN KEY (`train_id`) REFERENCES `trains` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `trm_trainrun` FOREIGN KEY (`train_run_id`) REFERENCES `train_runs` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=9928724 DEFAULT CHARSET=utf8;

我们有很多查询都按customer_id和timestamp筛选,因此我们为此创建了一个组合索引。

现在,我有一个简单的查询:

SELECT * FROM `train_run_messages` WHERE `customer_id` = '5' AND `timestamp` >= '2013-12-01 00:00:57' AND `timestamp` <= '2013-12-31 23:59:59' LIMIT 0, 100 

在我们目前有约1000万个条目的机器上,此查询大约需要16秒钟,这对我来说很长,因为有一个类似这样的查询的索引。

因此,让我们看一下该查询的explain输出:

+----+-------------+--------------------+------+-------------------------------------------    +--------------------+---------+-------+--------+-------------+
| id | select_type | table              | type | possible_keys                             | key                | key_len | ref   | rows       | Extra       |
+----+-------------+--------------------+------+-------------------------------------------+--------------------+---------+-------+--------+-------------+
|  1 | SIMPLE      | train_run_messages | ref  | timestamp,customer_timestmap,trm_customer | customer_timestamp | 4       | const | 551405     | Using where |
+----+-------------+--------------------+------+-------------------------------------------+--------------------+---------+-------+--------+-------------+

所以MySQL告诉我它将使用customer_timestamp索引,很好! 为什么查询仍需要约16秒? 由于我并不总是信任MySQL查询分析器,因此可以使用强制索引进行尝试:

SELECT * FROM `train_run_messages` USE INDEX (customer_timestamp) WHERE `customer_id` = '5' AND `timestamp` >= '2013-12-01 00:00:57' AND `timestamp` <= '2013-12-31 23:59:59' LIMIT 0, 100 

查询时间:0.079s !!

我:不解!

谁能解释为什么MySQL显然没有使用它表示从EXPLAIN输出使用的索引? 在执行真实查询时,有什么方法可以证明它真正使用了什么索引?

顺便说一句:这是慢日志的输出:

# Time: 131217 11:18:04
# User@Host: root[root] @ localhost [127.0.0.1]
# Query_time: 16.252878  Lock_time: 0.000168 Rows_sent: 100  Rows_examined: 9830711
SET timestamp=1387275484;
SELECT * FROM `train_run_messages` WHERE `customer_id` = '5' AND `timestamp` >= '2013-12-01 00:00:57' AND `timestamp` <= '2013-12-31 23:59:59' LIMIT 0, 100;

尽管没有明确指出它没有使用任何索引,但Rows_examined建议它执行完整的表扫描。

那么,在不使用USE INDEX的情况下可以解决此问题吗? 我们正在将Propel用作ORM,并且如果不手动编写查询,当前无法使用特定于MySQL的“ USE INDEX”。

编辑:这是EXPLAIN和USE INDEX的输出:

+----+-------------+--------------------+-------+--------------------+--------------------+---------+------+--------+-------------+
| id | select_type | table              | type  | possible_keys      | key                | key_len | ref  | rows   | Extra       |
+----+-------------+--------------------+-------+--------------------+--------------------+---------+------+--------+-------------+
|  1 | SIMPLE      | train_run_messages | range | customer_timestmap | customer_timestmap | 8       | NULL | 191264 | Using where |
+----+-------------+--------------------+-------+--------------------+--------------------+---------+------+--------+-------------+

MySQL有三个候选索引

  • (时间戳)
  • (customer_id,时间戳记)
  • (顾客ID)

你在问

`customer_id` = '5' AND `timestamp` BETWEEN ? AND ?

优化器从统计信息中选择(customer_id, timestamp)

InnoDB Engine的优化器取决于打开表时使用采样的统计信息。 默认采样在索引文件上读取8页。

所以, 我建议三件事如下

  1. 增加innodb_stats_sample_pages=64
  2. 删除冗余索引。 以下索引就可以了。 目前只有customer_id = 5(您说过)
    • (时间戳)
    • (顾客ID)
  3. 运行OPTIMIZE TABLE train_run_messages以重新组织表。
    • 这会减小表和索引的大小,有时这会使优化器更智能

对我而言,最大的问题是您用引号将客户ID包装起来时失败了,例如='5'。 通过这样做,它不能使用客户/时间戳索引,因为客户ID需要转换为字符串以匹配您的'5'vs just = 5,并且您应该可以使用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM