前陣子在跟同事討論一段 SP(Stored Procedure)的效能問題,話題漸漸轉到了 Table 建立時的設計策略。
同事的想法是這樣的:「建 Table 的時候就把欄位結構跟 Index 都設定好,之後 Insert 資料就一步到位,感覺比較有效率。」
而我的直覺剛好相反:「不對吧,Index 本身會影響 Insert 的速度,應該是先把資料塞完,最後再建 Index?」
兩個人都有點不確定,但認知方向剛好相反,就決定一起查清楚;結果,我的印象是對的,但要加一些但書。
為什麼 Index 會讓 Insert 變慢?
要解釋這件事,得先聊聊資料庫最常用的 Index 結構:B-Tree。
B-Tree 是什麼?
B-Tree 是大多數關聯式資料庫(PostgreSQL、MySQL、SQL Server)預設使用的 Index 結構,它長這樣:
[50]
/ \
[25] [75]
/ \ / \
[10] [40] [60] [90]
重點有三個:
- 樹的每一層都保持平衡:葉子節點都在同一深度
- 資料有排序:從左到右值是遞增的
- 查詢速度快:找一筆資料需要
O(log n)次比較
因為 B-Tree 要維持排序與平衡的特性,每次 Insert 一筆資料,都必須同時更新 B-Tree 的結構。
Insert 時到底發生什麼事?
每當你 INSERT 一筆新資料,資料庫不只是把那筆資料寫進去而已,它還要:
- 找到這筆資料在 B-Tree 中對應的位置
- 把新資料插入正確的節點
- 如果節點滿了,觸發「節點分裂(node split)」,重新調整樹的結構
- 必要時,更新上層節點的資訊
如果你有 3 個 Index,就得重複這個過程 3 次;資料量越大,這個效能開銷就越明顯。
先 Insert 再建 Index,好在哪裡?
當你先塞完所有資料、再一次建立 Index 時,資料庫可以走「批次建立」的路線:
- 一次讀完所有資料,做整體排序
- 從排序後的資料直接建 B-Tree,不需要一邊插入一邊調整平衡
- 最終結果就是一棵完美平衡的 B-Tree,而且 I/O 操作少非常多
就像整理一本書的目錄,是邊寫邊整理目錄比較快,還是全部寫完最後一次整理比較快?應該是後者會比較快。
業界怎麼說?
這不只是直覺,主流資料庫社群有相關建議:
PostgreSQL:
大量 Insert 前,建議先 DROP INDEX,Insert 完成後再重建,原因是大量 Insert 的效能會受到既有 Index 顯著影響。
MySQL:
DISABLE KEYS 的優化原理就是先在記憶體中建立 Index tree,最後再一次寫入磁碟,避免大量 disk seek,這比逐筆 Insert 時維護 Index 快非常多。
那……什麼時候應該先建 Index?
這裡有個重要的但書,我的同事不是完全錯的:
情境 | 建議做法 |
|---|---|
全新 Table,大量初始資料載入 | 先 Insert,後建 Index |
Table 已有大量舊資料,追加少量 | 保留 Index 直接 Insert(省掉重建成本) |
線上系統持續寫入 | Index 必須常駐,無法拿掉 |
一次插入量不到 Table 的 10~20% | 直接 Insert 就好,重建 Index 反而多此一舉 |
所以「先建 Index 再 Insert」在日常開發的線上系統場景,其實是正常的做法;問題主要出現在大批量資料載入(ETL、資料遷移、初始化)的情境。
操作範例
PostgreSQL
-- 大量載入前,先移除 Index
DROP INDEX idx_user_email;
-- 批次 Insert 資料
INSERT INTO users (id, email, name)
SELECT ...;
-- 資料載入完畢,重建 Index
CREATE INDEX idx_user_email ON users(email);
-- 記得更新統計資訊
ANALYZE users;
MySQL(InnoDB 版)
-- 關閉外鍵檢查(如果有的話)
SET FOREIGN_KEY_CHECKS = 0;
-- 批次插入
INSERT INTO orders (...) VALUES (...), (...), ...;
-- 恢復外鍵檢查
SET FOREIGN_KEY_CHECKS = 1;
-- InnoDB 大批量建議搭配使用 LOAD DATA INFILE,比 INSERT 快很多
LOAD DATA INFILE 'file_name' INTO TABLE orders ...;
跟同事的這場小討論,讓我重新整理了一個平常可能不太會注意到的細節。
- Index 的存在確實會讓
INSERT變慢,因為每次寫入都要同時維護 B-Tree 結構 - 大批量資料載入時,先 Insert 再建 Index 更有效率
- 日常的線上系統,Index 就讓它常駐就好,不需要特別處理
在大多數已上線系統的日常開發場景,先建好 Index 再 Insert 是沒有問題的;不過,如果在做的是 ETL、資料遷移、或是系統初始化,這個細節就得特別注意了。




















