這個(gè)選擇表示由于排序的原因再進(jìn)行了一次索引選擇優(yōu)化,由于我們的 SQL 使用了 id 排序(order by id asc limit 1),優(yōu)化器最終選擇了 PRIMARY 也就是全表掃描來執(zhí)行,也就是說這個(gè)選擇會(huì)無視之前的基于索引成本的選擇,為什么會(huì)有這樣的一個(gè)選項(xiàng)呢,主要原因如下:
The short explanation is that the optimizer?thinks?— or should I say?hopes?— that scanning the whole table (which is already sorted by the id field) will find the limited rows quick enough, and that this will avoid a sort operation. So by trying to avoid a sort, the optimizer ends-up losing time scanning the table.
從這段解釋可以看出主要原因是由于我們使用了 order by id asc 這種基于 id 的排序?qū)懛?,?yōu)化器認(rèn)為排序是個(gè)昂貴的操作,所以為了避免排序,并且它認(rèn)為?limit n 的 n 如果很小的話即使使用全表掃描也能很快執(zhí)行完,所以它選擇了全表掃描,也就避免了 id 的排序(全表掃描其實(shí)就是基于 id 主鍵的聚簇索引的掃描,本身就是基于 id 排好序的)
如果這個(gè)選擇是對(duì)的那也罷了,然而實(shí)際上這個(gè)優(yōu)化卻是有 bug 的!實(shí)際選擇 idx_uid_stat 執(zhí)行會(huì)快得多(只要 28 ms)!網(wǎng)上有不少人反饋這個(gè)問題,而且出現(xiàn)這個(gè)問題基本只與 SQL 中出現(xiàn)?order by id asc limit n這種寫法有關(guān),如果 n 比較小很大概率會(huì)走全表掃描,如果 n 比較大則會(huì)選擇正確的索引。
這個(gè) bug 最早追溯到 2014 年,不少人都呼吁官方及時(shí)修正這個(gè)bug,可能是實(shí)現(xiàn)比較困難,直到 MySQL 5.7,8.0 都還沒解決,所以在官方修復(fù)前我們要盡量避免這種寫法,如果一定要用這種寫法,怎么辦呢,主要有兩種方案
這種方案也可以讓優(yōu)化器選擇正確的索引,更推薦!為什么這個(gè) trick 可以呢,因?yàn)榇?SQL 雖然是按 id 排序的,但在 id 上作了加法這樣耗時(shí)的操作(雖然只是加個(gè)無用的 0,但足以騙過優(yōu)化器),優(yōu)化器認(rèn)為此時(shí)基于全表掃描會(huì)更耗性能,于是會(huì)選擇基于成本大小的方式來選擇索引