MySQL InnoDB 排序問題
我很驚訝地看到 MySQL 的 InnoDB 表現出一種有趣的行為,我無法完全解釋。根據官方 MySQL InnoDB 文件:
“除聚集索引之外的所有索引都稱為二級索引。在 InnoDB 中,二級索引中的每條記錄都包含該行的主鍵列,以及為二級索引指定的列。InnoDB 使用此主鍵值來在聚集索引中搜尋行”
所以據我了解,任何單列索引實際上都是選定列和主鍵的複合索引(如果不是這種情況,請糾正我)。因此,如果我從按索引列過濾並按主鍵排序的表中進行選擇,它應該是不需要文件排序的有效操作。
然而,在實踐中,情況並非如此,如下所示:
mysql> describe object_settings; +-------------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------------+--------------+------+-----+---------+----------------+ | object_setting_id | int(11) | NO | PRI | NULL | auto_increment | | object_id | int(11) | NO | MUL | NULL | | | name | varchar(50) | YES | MUL | NULL | | | value | varchar(255) | YES | | NULL | | +-------------------+--------------+------+-----+---------+----------------+ 4 rows in set (0.00 sec) mysql> select * from object_settings; +-------------------+-----------+------+-------+ | object_setting_id | object_id | name | value | +-------------------+-----------+------+-------+ | 1 | 10 | foo | bar | | 2 | 10 | bar | foo | | 3 | 11 | bar | foo | | 4 | 12 | bar | foo | | 5 | 13 | bar | foo | +-------------------+-----------+------+-------+ 5 rows in set (0.00 sec) mysql> explain select * from object_settings where object_id = 10 order by object_setting_id DESC; +----+-------------+-----------------+------+---------------+-----------+---------+-------+------+-----------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-----------------+------+---------------+-----------+---------+-------+------+-----------------------------+ | 1 | SIMPLE | object_settings | ref | object_id | object_id | 4 | const | 2 | Using where; Using filesort | +----+-------------+-----------------+------+---------------+-----------+---------+-------+------+-----------------------------+ 1 row in set (0.00 sec)
為什麼突然出現文件排序操作?還是 MySQL 文件的含義與我的理解完全不同?
我有一些相當令人痛心的消息:ORDER BY 仍然會對文件排序造成嚴重破壞。
隨著關於這個問題的所有炒作得到解決和修復,根本沒有辦法讓 InnoDB 有效地使用 ORDER BY 上的索引。
從 InnoDB 行數據的歸零開始,即聚集索引。
行標記為
- 一個 6 字節的交易 ID 欄位
- 一個 7 字節的滾動指針欄位
行傾向於按輸入數據的任何順序進行排序。PRIMARY KEY 的列包含在二級索引中,用於搜尋聚集索引中的行。不幸的是,這兩個 ID 欄位並沒有真正用於指示聚集索引中行的任何順序。(有關更多資訊,請參閱有關 InnoDB 物理行結構的 MySQL 文件)
這裡有一些更令人不安的事情:您知道您可以通過 PRIMARY KEY 的列或您選擇的任意順序對錶中的行進行排序嗎?
這是語法:
ALTER TABLE tblname ORDER BY col_name [, col_name] ...
這可以加快一些 PRIMARY KEY 排序的查詢,但令人不安的是它只適用於 MyISAM 表。為什麼不是 InnoDB ?
根據MySQL 文件
ALTER TABLE ... ORDER BY
:ORDER BY 使您能夠創建具有特定順序的行的新表。請注意,插入和刪除後,表不會保持此順序。此選項主要在您知道大部分時間以特定順序查詢行時很有用。通過在對錶進行重大更改後使用此選項,您可能可以獲得更高的性能。在某些情況下,如果表按您稍後要對其排序的列進行排序,則可能會使 MySQL 更容易排序。
ORDER BY 語法允許為排序指定一個或多個列名,每個列名可選地後跟 ASC 或 DESC 以分別指示升序或降序排序。預設為升序。只允許列名作為排序條件;不允許任意表達。該子句應在任何其他子句之後最後給出。
ORDER BY 對 InnoDB 表沒有意義,因為 InnoDB 總是根據聚集索引對錶行進行排序。
這對我來說並不奇怪,因為我在之前的一篇文章中提到了這一點:(
Aug 29, 2011
:按指定列對錶格進行預排序)因此,
ORDER BY
由於其內部索引組織,對 InnoDB 表執行操作永遠不能保證正確的索引選擇。因此,無論該表具有什麼二級索引,都不應對 InnoDB 表上的文件排序感到驚訝。
我在 5.6.14 (Ubuntu) 上對此進行了測試,結果是一樣的。ORDER BY導致文件排序。為什麼會這樣?
參考:http ://dev.mysql.com/doc/refman/5.6/en/order-by-optimization.html
在某些情況下,MySQL不能使用索引來解析 ORDER BY,儘管它仍然使用索引來查找與 WHERE 子句匹配的行。這些情況包括:
……
用於獲取行的鍵與 ORDER BY 中使用的鍵不同:
SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
所以答案是,用於獲取行的鍵與 ORDER BY 中使用的鍵不同,因此:
.... where object_id = 10 order by object_setting_id DESC;
滿足這種情況。