如果你曾經等得太久才等到資料庫查詢結束,你就知道性能緩慢的痛苦。
好消息是?索引可以來拯救你!
PostgreSQL 功能強大,但如果沒有 適當的索引,你的資料庫就像是在馬鈴薯上運行。
索引是 超快速查詢 的秘訣,但它們也有取捨。
PostgreSQL 中的索引 像書中的索引一樣——而不是掃描每一頁(每一行),你可以直接跳到相關的部分。
沒有索引的話,PostgreSQL 會執行 順序掃描,這意味著需要查看 每一行 來找到匹配項,對性能來說並不理想。
索引特別適用於:
WHERE
子句的查詢。有了索引,查詢的執行速度將是 對數級別快,而不是線性。
想像一下從 O(n) → O(log n) 的複雜度。
但是... 索引並不是萬能的。它們有取捨。
它們會消耗儲存空間,並可能減慢寫入速度。
因此,你需要聰明地決定何時何地使用它們。
PostgreSQL 的預設索引類型是 B-樹 (平衡樹)。
把它想像成一個樹狀結構,保持數據排序以便快速搜尋。它的工作原理如下:
例如,若你要在一個表中搜尋名稱「Mac」,B-樹將會:
這個過程減少了所需的比較次數,使搜尋變得 對數級別快。
當索引能夠 顯著 提高讀取性能時使用索引。常見情況包括:
情況 | 索引如何有助於? |
---|---|
按唯一字段搜尋(例如 id 、email ) |
更快的查詢,避免全表掃描 |
使用 WHERE 子句進行過濾 |
快速找到匹配行 |
排序(ORDER BY ) |
改善排序性能 |
聯接大型表 | 避免掃描整個數據集 |
全文搜尋 | 高效查找文本中的關鍵字 |
外鍵 | 確保快速的關係檢查 |
索引並不是免費的。
每次你 插入、更新 或 刪除 數據時,索引都需要更新。
這可能會減慢寫入密集的工作負載。如果:
PostgreSQL 的 MVCC 機制可能會導致「僅堆積行」的更新,造成死行並增加 I/O。
在盲目添加索引之前,測試它們是否實際有幫助。
PostgreSQL 提供了 EXPLAIN ANALYZE 來分析查詢執行時間。試試這個:
EXPLAIN ANALYZE SELECT * FROM users WHERE email = '[email protected]';
查找 Seq Scan
(不好)與 Index Scan
(好)。
如果添加索引不減少查詢時間,那就不值得。
想刪除無用的索引?使用:
DROP INDEX index_name;
並非所有索引都是一樣的。PostgreSQL 提供了幾種類型:
幾乎所有資料庫都會有一些 B-樹索引。
B-樹試圖保持平衡,樹的每個分支中的數據量大致相同。
因此,必須遍歷的層數始終在相同的範圍內。
B-樹索引可以有效用於等值和範圍查詢。
它們可以操作所有數據類型,並且也可以用於檢索 NULL 值。
B-樹非常適合緩存,即使僅部分緩存時也能很好的運作。
=
、<
、<=
、>
、>=
)。CREATE INDEX idx_users_email ON users(email);
在 PostgreSQL 10 之前僅對等值比較有用,但因為它們不具有事務安全性,崩潰後需要手動重建,而且不會複製到從屬節點,因此幾乎不建議使用。
在 PostgreSQL 10 及以上版本中,哈希索引現在支援寫前日誌並可複製到從屬節點。
=
)。>
、<
)。CREATE INDEX idx_users_hash_email ON users USING hash(email);
當一個索引必須將多個值映射到一行時,GIN 非常有用,而 B-樹索引則是優化為當一行只有一個鍵值時使用。
GIN 很適合用於索引數組值以及實現全文搜尋。
CREATE INDEX idx_users_bio ON users USING gin(to_tsvector('english', bio));
GiST 索引允許你建立一般的平衡樹結構,並可以用於超越等值和範圍比較的操作。
它們用於索引幾何數據類型以及全文搜尋。
CREATE INDEX idx_locations ON places USING gist(location);
對於 大型、序列化存儲的數據(如時間序列數據)來說非常有效。
CREATE INDEX idx_logs_timestamp ON logs USING brin(timestamp);
當多個欄位常常一起查詢時,索引多個欄位。
CREATE INDEX idx_orders_user_date ON orders(user_id, order_date);
只有 B-樹、GiST、GIN 和 BRIN 索引類型支持多鍵欄位索引。
是否有多個鍵欄位與是否能將 INCLUDE 欄位添加到索引是獨立的。
索引最多可以有 32 列,包括 INCLUDE 欄位。
僅索引一部分數據以節省空間。
CREATE INDEX idx_active_users ON users(email) WHERE is_active = true;
部分索引是一種基於表格子集建立的索引;子集由條件表達式定義。
索引僅包含滿足條件的表行的條目。
儲存附加欄位以避免訪問主表。
CREATE INDEX idx_orders_covering ON orders(user_id, order_date) INCLUDE (total_price);
在 PostgreSQL 中,所有索引都是二級索引,這意味著每個索引與表的主數據區域分開儲存。
有效地確保欄位唯一性。
CREATE UNIQUE INDEX idx_unique_email ON users(email);
索引也可以用來強制每個欄位值的唯一性,或多個欄位組合值的唯一性。
只有 B-樹索引可以聲明為唯一的。
動作 | 索引的影響 |
---|---|
讀取 | ✅ 查詢更快 |
寫入(INSERT/UPDATE/DELETE) | ❌ 由於索引維護而變慢 |
儲存 | ❌ 需要更多磁碟空間 |
清理 | ❌ 死元組需要清理 |
如果你的應用是 讀取密集型,索引是理所當然的選擇。如果它是 寫入密集型,則需要謹慎選擇。
索引是 PostgreSQL 中 性能增強的最大利器之一。明智地使用它們:
我正在開發一個非常方便的工具 LiveAPI。
LiveAPI 幫助你在幾分鐘內記錄所有後端 API。
使用 LiveAPI,你可以快速生成互動式 API 文件,讓用戶可以直接從瀏覽器執行 API。
如果你厭倦了手動創建 API 文件,這個工具可能會讓你的生活更輕鬆。
原文出處:https://dev.to/lovestaco/speed-up-db-queries-like-a-pro-2d95