[英]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` = '5' AND `timestamp` BETWEEN ? AND ?
優化器從統計信息中選擇(customer_id, timestamp)
。
InnoDB Engine的優化器取決於打開表時使用采樣的統計信息。 默認采樣在索引文件上讀取8頁。
所以, 我建議三件事如下
innodb_stats_sample_pages=64
。
OPTIMIZE TABLE train_run_messages
以重新組織表。
對我而言,最大的問題是您用引號將客戶ID包裝起來時失敗了,例如='5'。 通過這樣做,它不能使用客戶/時間戳索引,因為客戶ID需要轉換為字符串以匹配您的'5'vs just = 5,並且您應該可以使用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.