接著上一張 N+1 講。N+1 是「同一件事問了 101 次」;這篇要講的是另一種慢——你只問了一次,但這一次就跑超久

情境長這樣:一個用 email 找使用者的查詢,WHERE email = ?,簡單到不行。開發的時候秒回,上線幾個月、使用者表長到幾十萬筆之後,同一句查詢開始要跑好幾秒。SQL 沒改、邏輯沒改,就只是資料變多了

差別在哪?多數時候,就差一個 index。


先用「翻書」搞懂它

想像你手上一本沒有目錄的字典,我要你找「跩」這個字。你只能怎麼辦?從第一頁開始一頁一頁翻,翻到為止。這就是 DB 沒有 index 時做的事——叫全表掃描(Seq Scan),為了找符合條件的那幾筆,它把整張表從頭到尾翻一遍。

現在這本字典有目錄了。你要找「跩」,先翻目錄查到它在第 387 頁,直接跳過去。你不用翻過前面 386 頁。 這就是 index。

index 底層通常是一種叫 B+ tree 的結構,但你不用記它的實作細節,只要抓住那個直覺就好:它讓「找一筆」從「翻完整本」變成「查目錄跳過去」。 用 CS 的話講,是從 O(n)(資料幾筆就翻幾筆)變成 O(log n)(翻個幾層就到)。


所以到底差多少

關鍵就在這個「線性 vs 對數」的差距,而且資料越多,差距越誇張

  • 表裡只有 10 筆:有沒有 index 根本沒差,DB 一眼掃完,你感覺不到。
  • 表裡有 100 萬筆:沒 index 它要平均翻 50 萬筆才找到;有 index(B+ tree)大概翻 20 層內就到。

這不是「快一點」,是差好幾個數量級。而且這正是為什麼 index 問題跟 N+1 一樣——開發時完全沒感覺,資料長大才爆。你 dev 環境塞 10 筆假資料,Seq Scan 跟 Index Scan 都是一瞬間,你怎麼可能發現?等 production 資料堆到幾十萬、那個沒 index 的 WHERE 才會原形畢露。

所以「差多少」沒有一個固定數字——它跟你的資料量直接掛鉤。資料越大,那個沒加的 index 就讓你賠得越慘。


重點:怎麼自己驗有沒有走 index

跟 N+1 一樣,你不能用「猜」的,要用看的。資料庫給了你一個 X 光機——EXPLAIN

把它擺在你的查詢前面,DB 會告訴你它打算怎麼執行這條 query(不會真的跑);想連實際耗時也一起看,就用 EXPLAIN ANALYZE(這個會真的跑一次):

EXPLAIN ANALYZE
SELECT * FROM users WHERE email = '[email protected]';

你要在輸出裡找的,就一個關鍵字:

你看到意思該怎樣
Seq Scan(全表掃描)DB 正在「一頁一頁翻整張表」⚠️ 如果這張表很大,這就是你的兇手——該加 index 了
Index Scan(索引掃描)DB 有走 index,直接跳到目標✅ 這就是你要的

再順手看一下它估的 rows——如果一個「只想撈一筆」的查詢,DB 卻說它要掃幾十萬 rows,那幾乎可以確定它在做全表掃描。看到大表上的 Seq Scan,就是該加 index 的信號。

加法本身很單純:

CREATE INDEX idx_users_email ON users (email);

加完再 EXPLAIN 一次,看它從 Seq Scan 變成 Index Scan,你就親眼驗證了這個 index 有效。


但 index 不是加越多越好

看到這你可能想「那我每個欄位都加 index 不就天下無敵」——別,這又是另一個坑:

  • 寫入會變慢:每次 insert / update,DB 都得順便維護所有相關的 index。你加越多 index,寫入就被拖越慢。index 是拿「寫的成本」換「讀的速度」。
  • 複合索引有順序講究(a, b) 的索引能幫 WHERE aWHERE a AND b,但幫不了只查 WHERE b 的——這叫最左前綴原則。
  • 有些寫法天生用不到 index:最經典的就是 LIKE '%關鍵字'(開頭是萬用字元),目錄幫不了你,只能乖乖全表掃。

這些更深的設計取捨,留給專門的索引文章:

個人經驗:我自己的經驗其實超單純——某張表查詢慢,加了 index 之後速度「咻」一下就上去了,就這樣。但老實講,我以前都是憑感覺加:覺得這個欄位常被查就加個 index,加完看頁面「好像有變快」就收工,從來沒真的用 EXPLAIN 去驗過它到底有沒有走 index

寫這篇我才意識到中間有個洞——「感覺有變快」跟「DB 真的用了那個 index」是兩回事。你可能加了一個根本沒被用到的 index(白白拖慢寫入),也可能那次變快其實是別的原因。EXPLAIN 就是把「感覺」換成「親眼看到 Seq Scan 變成 Index Scan」。所以如果你跟過去的我一樣靠感覺加 index——這篇就是想說服你,多花那 10 秒照一下,真的值得。


接下來往哪走

這篇想給你的是:看到查詢變慢,先懷疑 index、而且會自己用 EXPLAIN 驗。再深的:


反思

index 的觀念其實樸素到不行——給你的表一個目錄,DB 就不用每次都翻完整本書。 難的不是理解它,是記得去檢查

因為它跟 N+1 是同一種陰險:dev 環境永遠看不出來,等 production 資料夠大才咬你一口。

所以每次寫一個「會用 WHERE / JOIN / ORDER BY 去撈大表」的查詢,養成一個動作就好——上線前,拿 EXPLAIN 照它一下

  1. 它走的是 Index Scan 還是 Seq Scan?
  2. 如果是 Seq Scan,這張表將來會長到多大?小表無所謂,大表就是埋雷。

照一下花你 10 秒,但能讓你少一個「幾個月後莫名其妙變慢、還查不出原因」的半夜。