Mysql

MySQL InnoDB 排序問題

  • September 8, 2021

我很驚訝地看到 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;

滿足這種情況。

引用自:https://dba.stackexchange.com/questions/45151