Mysql

為什麼這個 UPDATE 查詢不使用索引?

  • October 19, 2021

我有一個查詢:

EXPLAIN UPDATE myTable SET col2 = 'foo' WHERE col1 = 'bar';
+----+-------------+-------------+------------+-------+-----------------------+---------+---------+------+--------+----------+-------------+
| id | select_type | table       | partitions | type  | possible_keys         | key     | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-------------+------------+-------+-----------------------+---------+---------+------+--------+----------+-------------+
|  1 | UPDATE      | myTable     | NULL       | index | myTable_ind_indx1     | PRIMARY | 4       | NULL | 890860 |   100.00 | Using where |
+----+-------------+-------------+------------+-------+-----------------------+---------+---------+------+--------+----------+-------------+

它使用PRIMARYkey 而不是使用正確的 index myTable_ind_indx1。這是表創建語句的相關部分:

CREATE TABLE `myTable` (
 `id` int NOT NULL,
 ...
 `col1` varchar(64) DEFAULT NULL,
 ...
 PRIMARY KEY (`id`),
 KEY `myTable_ind_indx1` (`col1`,`col3`),
 ...
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1

所以它正在執行全表掃描,而不是使用正確的索引。起初我認為這是因為查詢規劃器估計行數約為 900k,總行數約為 1M,但如果我計算與 WHERE 子句匹配的行數,它實際上是 ~250k,而且甚至添加了USE INDEX準確的結果相同的計劃。將查詢更改為SELECT使用正確索引的結果。這是在 MySQL 8.0.19 上。關於我可以做些什麼來改善事情的任何想法?

如果條件col1 = 'bar'匹配 1M 中的 250k 行,即表的25%,這意味著更新將修改表的幾乎所有頁面,因為幾乎每個頁面都會包含至少一個匹配條件的行(除非之間有直接聯繫但是優化idcol1無論如何都不知道)。

而且當它必須重寫幾乎整個表時,沒有辦法使用索引隨機跳過比只進行全表掃描更好。優化器正在選擇正確的計劃。

id如果這些行實際上聚集在表的一部分中(和之間存在一些相關性),那麼您可以通過將範圍添加到子句col1來告訴優化器。但是沒有它,全表掃描只是最快的方法。id``WHERE

對於 select 它可能會有所不同,因為它不必將內容寫入磁碟,它可能會使用不同的 seq/rand 訪問*“價格”*並更多地利用記憶體。因此,沒有類似的計劃SELECT並且UPDATE可能​​是出乎意料但有效的。

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