🎯 什麼情境該想到我

當你的 WHERE col LIKE '%keyword%' 慢到不能用,而且你發現加了 B-tree 索引也沒用的時候。

原因:B-tree 只能從字串開頭比對,所以 LIKE 'abc%' 走得到索引,LIKE '%abc%' 走不到——它只能全表掃描。

⚙️ 怎麼用(步驟 / 公式)

1. 裝擴充

CREATE EXTENSION IF NOT EXISTS pg_trgm;

它是 trusted 模組——有該資料庫 CREATE 權限的一般使用者就能裝,不需要超級使用者。

2. 建索引,先決定 GiST 還是 GIN

-- GIN:查詢較快,索引較大、建得較慢。一般模糊查詢的預設選擇
CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops);
 
-- GiST:索引較小,且支援 KNN 排序
CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops);

決定性的功能差異,不只是效能
ORDER BY t <-> 'word' LIMIT 10(取最相近的 K 筆)GiST 做得很有效率,GIN 做不到
要做「最相近前 N 筆」就選 GiST;只做布林式的比對就用 GIN。

GiST 還可調簽章長度(預設 12 bytes,範圍 1–2024):

CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops(siglen=32));

越長越精準(掃更少索引與 heap page),代價是索引更大。

3. 這些查詢現在都能走索引了

SELECT * FROM test_trgm WHERE t LIKE '%foo%bar';   -- 9.1 起
SELECT * FROM test_trgm WHERE t ILIKE '%foo%';
SELECT * FROM test_trgm WHERE t ~ '(foo|bar)';     -- 正規表達式,9.3 起

運作方式是從搜尋字串(或正規表達式)抽出 trigram,再去索引裡查
搜尋字串抽得出的 trigram 越多,索引越有效。

4. 驗證它真的有用到

EXPLAIN ANALYZE SELECT * FROM test_trgm WHERE t LIKE '%foo%';

沒看到 Bitmap Index Scan 就是沒吃到索引——多半是下一節的原因。

🧪 我實際套用的紀錄

  • 2026-09-01:(待填)

⚠️ 注意 / 什麼時候不適用

  • 🔴 抽不出 trigram 的樣式會退化成全索引掃描(文件原文)。
    記住 trigram 的取法:每個詞前補兩空格、後補一空格,'cat'" c"" ca""cat""at "
    所以搜尋字串太短(一兩個字元)幾乎沒有效果LIKE '%a%' 這種等於沒索引。
  • 非英數字元會被忽略'foo|bar' 被當成兩個詞處理。
  • 不支援不等式運算子;而且做等值比對時未必比 B-tree 有效率——需要等值查詢就照常建 B-tree,兩種索引可以並存。
  • 預設編譯下比較是不分大小寫的,所以 LIKEILIKE 行為會比你預期的接近。
  • 索引不是免費的:GIN 索引在寫入密集的表上維護成本明顯,上線前量寫入延遲。
  • ⚠️ 中日韓文字的效果我沒有驗證過。文件只說「對許多自然語言有效」,沒有針對 CJK 說明;中文沒有空格分詞,trigram 的切法與效果要拿你自己的資料實測再決定,別直接假設可用。

🔗 相關工具