10月7日
查食材不用翻整個冰庫:一題資料庫面試題,廚房早就知道答案
牛客網上一題「index 和 all 都是全量掃描,index 快在哪」的面試討論,被我們翻譯成廚房裡帳本與食材櫃、貼好標籤的調味架與一鍋免翻找的晚餐故事。
十月上旬,牛客網上一篇討論串被工程師們來回點閱,題目是:「那 index 和 all 都是全量掃描,index 比 all 快在哪裡?」這是 MySQL 面試的經典題,也是許多後端工程師反覆被問到的一關。討論串裡的解釋很清楚,但如果你家廚房夠亂過,其實你早就懂了。
兩種「全找一遍」,找的地方不一樣
先說結論。index 和 all 這兩種掃描方式,名字裡都有「全量」,但掃的東西不同。all 是掃整張資料表,走的是聚簇索引整棵樹,葉子上掛著完整的一行行資料,體積大。index 是掃某個二級索引的整棵樹,葉子上只有索引欄位值加主鍵值,體積小得多。
換到廚房裡是這樣的。你想知道今晚的雞湯還剩多少,all 的做法是打開冰箱、冷凍庫、乾貨櫃、米缸,把每一格都翻一遍;index 的做法是看牆上那張你手寫的庫存清單,幾行字,掃一眼就有了。兩種都是「全部看過」,但一種翻的是沈甸甸的實物,一種讀的是薄薄一張紙。討論串裡的比喻更妙:統計全班身高,一種是把每本厚厚的學生檔案搬出來翻,一種是直接念牆上的身高登記表。
搬運的東西小,花的力氣就少。資料庫說這叫 IO 開銷低,廚房說這叫省事。
:::
排好的東西,讀出來就是順的
第二個差別更有味道:有序。索引樹天生是排好序的,當 SQL 帶有 ORDER BY,而且查的欄位正好是索引欄位,資料讀出來就已經照你要的順序排好,直接端上桌。走 all 全表掃描的話,讀出來一團亂序,資料庫得在記憶體裡再做一次額外排序,也就是執行計畫裡那個昂貴的 Using filesort。
你家調味架也懂這件事。鹽、糖、醬油、醋照常用頻率和高矮排好,炒菜到一半伸手就拿到,不用回頭找。要是那天大掃除全收進一個箱子裡,煮一道菜就得整箱倒出來重排一次,菜在鍋裡等,火不等人。這也是為什麼秋冬醃漬季之前,很多家庭會先把玻璃罐貼上標籤、照醃的日期排好:取用的時候,順序本身就是省下來的時間。
為什麼有時候反而不要快的那條路
討論串裡最實用的一段,是講退化。當你寫 SELECT *,要查出所有欄位,如果還硬走二級索引全掃,每一行都得拿著主鍵回主索引裡「回表」查完整資料,產生大量隨機 IO,反而比直接全表掃描慢得多。所以 MySQL 的優化器在需要查很多欄位時,會果斷選 all。
這就像你只是想確認「蔥還有沒有」,看清單最快;但如果你要的是完整的配料包,每一樣都按清單去倉庫領一趟,來回跑斷腿,還不如一開始就把整個備料車推過來。快與慢從來不是看工具本身,而是看你要端上桌的是什麼。
這也是面試題讓人又愛又恨的地方,它考的不是背誦,是你有沒有真的想過一整條路徑的成本。牛客網上這類討論總在求職季特別熱鬧,就像我們之前寫過的那年在秋招裡一碗麵配著的焦慮,技術問題背後,往往是一段一邊喫泡麵一邊刷題的日子。
回到你的餐桌
那天晚餐後,如果你也順手把冰箱裡的剩菜貼上日期、把常拿的醬料移到前排,你其實就在替未來的自己建一條小小的索引。查得快,是因為找的東西變小了;喫得順,是因為擺的位置排好了。資料庫工程師花一整場面試在講的道理,一個把廚房理乾淨的人,身體早就記住了。