簡體   English   中英

為什么在MySQL中刪除此索引會加速我的查詢100倍?

[英]Why does removing this index in MySQL speed up my query 100x?

我有以下MySQL表(簡體):

CREATE TABLE `track` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(256) NOT NULL,
  `is_active` tinyint(1) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `is_active` (`is_active`, `id`)
) ENGINE=MyISAM AUTO_INCREMENT=7495088 DEFAULT CHARSET=utf8

“ is_active”列標記了我想在大多數(但不是全部)查詢中忽略的行。 我有一些查詢會定期從該表中讀取塊。 其中之一看起來像這樣:

SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10;

該查詢需要一分鍾的時間才能執行。 這是執行計划:

> EXPLAIN SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10;
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+
| id | select_type | table | type | possible_keys  | key    | key_len | ref   | rows    | Extra       |
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+
|  1 | SIMPLE      | t     | ref  | PRIMARY,is_active | is_active | 1       | const | 3747543 | Using where |
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+

現在,如果我告訴MySQL忽略'is_active'索引,查詢將立即發生。

> EXPLAIN SELECT id,title from track IGNORE INDEX(is_active) WHERE (track.is_active=1 AND track.id > 5580702) ORDER BY id ASC LIMIT 10;
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows    | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
|  1 | SIMPLE      | t     | range | PRIMARY       | PRIMARY | 4       | NULL | 1597518 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+

現在,真正奇怪的是,如果我強制MySQL使用'is_active'索引,查詢將立即再次發生!

+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows    | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
|  1 | SIMPLE      | t     | range | is_active     |is_active| 5       | NULL | 1866730 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+

我只是不了解這種行為。 在“ is_active”索引中,行應按is_active排序,后跟id。 我在查詢中同時使用了'is_active'和'id'列,因此似乎只需要在樹上繞一圈即可找到ID,然后使用這些ID從表中檢索標題。

這是怎么回事?

編輯:關於我在做什么的更多信息:

  • 查詢緩存已禁用
  • 運行OPTIMIZE TABLE和ANALYZE TABLE無效
  • 6,620,372行的'is_active'設置為True。 874714行的'is_active'設置為False。
  • 再次使用FORCE INDEX(is_active)可以加快查詢速度。
  • MySQL版本5.1.54

看來MySQL在如何使用索引方面做出了錯誤的決定。

從該查詢計划中可以看出,它可能使用了PRIMARY索引或is_active索引,並且選擇了is_active以便首先按track.is_active進行縮小。 但是,它僅使用索引的第一列(track.is_active)。 這樣就得到了3747543的結果,然后必須對其進行過濾和排序。

如果選擇了PRIMARY索引,則可以使用該索引將其范圍縮小到1597518行,並且已經按照track.id的順序對其進行了檢索,因此無需進一步排序。 那會更快。

新的消息:

在使用FORCE INDEX的第三種情況下,MySQL使用的是is_active索引,但現在不僅使用第一列,還使用了兩列(請參閱key_len)。 因此,它現在可以通過is_active進行縮小,並使用相同的索引按id進行排序和過濾,並且由於is_active是單個常量,因此第二列滿足ORDER BY(即,來自索引的單個分支的行已經排序)。 這似乎比使用PRIMARY更好,甚至可能是您最初打算的,對吧?

我不知道為什么沒有FORCE INDEX時不使用該索引的兩列,除非查詢之間以微妙的方式發生了變化。 如果不是的話,我會把它歸咎於MySQL做出錯誤的決定。

我認為加速是由於您的where子句。 我假設它只是在整個大型表中檢索行的一小部分。 在較小的子集上對is_active的檢索數據進行表掃描比通過大型索引文件進行過濾要快。 遍歷單列索引比遍歷組合索引快得多。

您可以嘗試的幾件事:

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM