CREATE TABLE `oplogs` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`kind` VARCHAR(45) NOT NULL DEFAULT '',
`op` VARCHAR(100) NOT NULL,
`user` VARCHAR(25) NOT NULL DEFAULT '',
`ip` VARCHAR(16) NOT NULL DEFAULT '',
`updatetime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=34896 DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED;
數據量:34,895 行
SELECT a.id, a.kind, a.op, a.user, a.ip, a.updatetime
FROM oplogs AS a
INNER JOIN (
SELECT id FROM oplogs WHERE 1 ORDER BY id DESC LIMIT 0, 20
) AS b USING (id);
| 環境 | 首次查詢耗時 | EXPLAIN 關鍵差異 |
|---|---|---|
| MySQL 5.5.38 (Linux) | ~0.7 秒 | 子查詢使用索引,rows 極少 |
| MySQL 5.6.19 | ~3.5 秒 | 子查詢全表掃描,rows = 全表行數 |
5.5.38(優秀):
id=2, select_type=DEPENDENT SUBQUERY, type=index, key=PRIMARY, rows=20
子查詢走主鍵索引倒序掃描,僅讀 20 行。
5.6.19(退化):
id=2, select_type=MATERIALIZED, type=ALL, key=NULL, rows=34895
子查詢被物化為臨時表,全表掃描 3.5 萬行,未使用索引。
5.6 引入子查詢物化與半連接優化,但對 ORDER BY ... LIMIT 子查詢的處理存在回歸:
ORDER BY ... LIMIT 的索引優化機會SELECT /*+ MAX_EXECUTION_TIME(1000) */ ... -- 或關閉物化 SET optimizer_switch = 'materialization=off';
SELECT a.*
FROM oplogs a
JOIN (
SELECT id FROM oplogs ORDER BY id DESC LIMIT 20
) b ON a.id = b.id;
將子查詢提升為衍生表,5.6 對衍生表優化較好。
SELECT * FROM oplogs
WHERE id IN (
SELECT id FROM oplogs ORDER BY id DESC LIMIT 20
);
MySQL 5.7 / 8.0 修復此回歸,子查詢優化器大幅重寫,ORDER BY LIMIT 子查詢可正常走索引。
EXPLAIN 關注 select_type(MATERIALIZED/DEPENDENT SUBQUERY)與 type(ALL/index)