CodeGym /課程 /SQL SELF /過度索引的問題

過度索引的問題

SQL SELF
等級 38 , 課堂 2
開放

Index 絕對是讓資料庫變快的超棒工具,但就像大家常說的:「太多反而不好」。不是每個 index 都有用,index 太多反而會拖累系統,幫倒忙。聽起來很矛盾對吧?我們來細細聊聊。

想像一下有個超大的圖書館,裡面有很多目錄可以找書——比如依作者、依類型、依出版年份。每個目錄都能幫你快點找到書。但如果目錄太多——像是每個書名的每個字、每個細節都做一個目錄——你會發現根本找不到東西:查找變慢、目錄佔空間,圖書館員還要一直更新這些清單,超累。

資料庫裡的 index 其實也差不多:它們幫你快點找到資料,但如果 index 太多,每次新增或修改資料時都要更新一堆 index,超麻煩。而且硬碟空間也會被吃爆。更慘的是,index 太多時,系統還會搞不清楚該用哪一個。

所以,跟圖書館的目錄一樣,index 真的不要亂加——有幾個夠用又有效的就好,沒必要搞一堆沒用的。

來玩個「PostgreSQL 偵探」遊戲。假設你對同一個欄位加了三個 index,因為你覺得這樣會變快。但想像一下:

  • 如果你的 table 是一個超大的學生名單,然後有三個 index,每加一個新學生就要更新三次 index。這哪裡快了?
  • 如果你有 10 個這種 table,每個都塞滿 index?整個資料庫效能直接 GG。

怎麼判斷你有沒有過度索引的問題?

第一步,先看看你現在有多少 index。在 PostgreSQL 裡可以用這個指令:

\d 表格名稱

這個指令會顯示 table、欄位還有相關的 index。如果你看到一個 table 綁了一堆 index,這就該警覺了。

另一個超好用的工具是系統視圖 pg_stat_user_indexes。它會顯示每個 index 被用過幾次,讓你知道哪些 index 根本是「死重」:

SELECT
    relname AS table_name,
    indexrelname AS index_name,
    idx_scan AS index_scans
FROM
    pg_stat_user_indexes
WHERE
    idx_scan = 0;

如果 idx_scan 是 0,代表這個 index 從來沒被用過。這種 index 就是刪掉的好對象。

過度索引的範例

假設有一個 users 的 table:

CREATE TABLE users (
    user_id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE,
    username VARCHAR(50),
    created_at TIMESTAMP DEFAULT NOW()
);

然後我們有三個 index:

-- email 的 index
CREATE INDEX idx_users_email ON users (email);

-- username 的 index
CREATE INDEX idx_users_username ON users (username);

-- created_at 的 index
CREATE INDEX idx_users_created_at ON users (created_at);

現在來看一下我們常做的查詢:

  1. 用 email 找 user。
  2. 用 username 找 user。
  3. created_at 排序 user。

看起來這些 index 很有用。但問題來了:如果這些查詢很少發生(比如一週才一次),那這些 index 根本沒必要。更慘的是,如果有些 index 根本沒被用過,它們只會拖慢 insert 跟 update。

舉例來說:假設 users table 裡有這些資料:

user_id email username created_at
1 alex.lin@mail.com alexlin 2024-06-15 10:23:00
2 anna.min@mail.com annamin 2024-06-16 12:47:00
3 otto.song@mail.com ottosong 2024-06-17 08:30:00
4 maria.chi@mail.com mariachi 2024-06-18 14:10:00

如果幾乎沒有人用 username 查詢,idx_users_username 這個 index 就完全沒被用過(idx_scan = 0),直接砍掉會更優化。

所以說,index 是很棒的工具,但要用得聰明。幾個有用又常用的 index 比一堆沒用的好多了。

怎麼避免過度索引

  1. 分析 index 的使用情況。 就像剛剛說的,用 pg_stat_user_indexes 看看 index 的使用統計。如果某個 index 幾乎沒被用過,直接砍掉:
DROP INDEX IF EXISTS index_名稱;
  1. 只對常用查詢加 index。 加 index 前先問自己幾個問題:
  • 這個欄位常常出現在 WHEREORDER BYGROUP BY 嗎?
  • table 裡的資料量很大嗎?
  • 沒加 index 查詢真的很慢嗎?

只要有一題答案是「不是」,那這個 index 可能就多餘了。

  1. 用複合 index。 如果你常常在一個查詢裡用到多個欄位,不要每個欄位都加一個 index,直接做一個複合 index:
CREATE INDEX idx_users_email_username ON users (email, username);

這樣查詢同時用 email username 就會快很多。

  1. 定期檢查現有的 index。 資料庫長大後,你的查詢可能會變。以前有用的 index,現在可能沒用了。記得定期檢查,把沒用的 index 清掉。

最小化 index 的範例

回到我們的 users table。與其三個 index,不如這樣優化:

  • 如果很少用 created_at 排序,就不要加這個 index。
  • emailusername 的 index 合併成一個複合 index:
CREATE INDEX idx_users_email_username ON users (email, username);

總結:平衡的祕訣是什麼?

就像寫 code 一樣,這裡也要走極簡風:「越少越好」。不要因為可以就每個欄位都加 index。想清楚你為什麼需要這個 index,它真的能讓查詢變快嗎?要務實一點,記住:厲害的工程師不是亂加 index,而是懂得怎麼用、什麼時候用。

現在你有這個工具,就能避免過度索引的災難,讓你的資料庫快得像獵豹,不會像背著一堆沒用 index 的烏龜一樣慢吞吞。

2
任務
SQL SELF, 等級 38, 課堂 2
上鎖
找出未被使用的索引
找出未被使用的索引
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION