今天同事在處理系統慢SQL時遇到幾個疑惑的問題,簡單描述如下~
【背景鋪墊】
相關表:
CREATE TABLE test_table ( id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, name varchar(32) NOT NULL, PRIMARY KEY (id) ) ENGINE = InnoDB CHARSET = utf8mb4;
test_table 表記錄數約12w+
問題描述
相關SQL:
EXPLAIN SELECT COUNT(*) FROM test_table WHERE id >= 10534 AND id <= 15375;

疑問1:上述SQL理應按id主鍵(聚簇索引)范圍查找,為啥explain里的rows會多余兩者之差呢?
在SQL結尾處增加 LIMIT 10 后,rows數值竟然沒有任何影響(覺得可能會變為: 10)。
EXPLAIN SELECT COUNT(*) FROM test_table WHERE id >= 10000 LIMIT 10;

疑問2:LIMIT值不會影響rows的值么?
rows究竟是怎么計算的呢?
這個rows在官網文檔中的解釋如下:
“
rows (JSON name: rows)
The rows column indicates the number of rows MySQL believes it must examine to execute the query.
For [InnoDB] tables, this number is an estimate, and may not always be exact.
http://dev.mysql.com/doc/refman/5.7/en/explain-output.html#explain_rows
簡單理解即:這個rows就是mysql認為估計需要檢測的行數。
為了探究rows究竟是如何算出來的,查找MYSQL源碼來看看:
文件1:sql/opt_explain_traditional.cc 關鍵部分:push(&items, column_buffer.col_rows, nil) 文件2:sql/opt_explain.cc 關鍵部分:select->quick->records 文件3:sql/opt_range.cc 關鍵部分:check_quick_select
而check_quick_select的功能,在MySQL源碼中的注釋為:
“
Calculate estimate of number records that will be retrieved by a range scan on given index using given SEL_ARG intervals tree.
簡單翻譯就是:這個方法僅僅根據給出的關於這個索引的條件和索引本身,來判斷需要掃描多少行。
總結
MySQL Explain 里的 rows 這個值
- 是MySQL認為它要檢查的行數(僅做參考),而不是結果集里的行數;
- 同時 SQL里的 LIMIT 和這個也是沒有直接關系的。
另外,很多優化手段,例如關聯緩沖區和查詢緩存,都無法影響到rows的顯示。MySQL可能不必真的讀所有它估計到的行,它也不知道任何關於操作系統或硬件緩存的信息。