Elasticsearch 搜尋引擎 (Elasticsearch)
Elasticsearch 是建立在 Apache Lucene 之上的分散式搜尋協調層。Lucene 負責「搜尋本身」(inverted index、doc values);Elasticsearch 負責「分散式那層」(cluster 協調、REST API、aggregation、即時能力)。
面試定位:搜尋層,不是真理來源(source of truth)。權威資料放別處,用 CDC 同步進 ES。
從使用端看:四個核心名詞
| 名詞 | 對應 RDBMS | 說明 |
|---|---|---|
| Document | row | 一筆被搜尋的資料,本質是任意 JSON 物件,有唯一 _id |
| Index | table | 一群 document 的集合;搜尋是針對 index 做的 |
| Mapping | schema | 定義有哪些 field、型別、如何被索引 |
| Field | column | document 裡的鍵值對 |
ES 的 Index(≈table) 和「讓查詢變快的輔助資料結構(inverted index)」是不同概念,靠上下文區分。
keyword vs text — mapping 最關鍵的選擇
| 型別 | 處理方式 | 查詢行為 | 用途 |
|---|---|---|---|
| keyword | 整段值,不分詞 | 精確比對(像 hash 表) | id、tag、category、排序 |
| text | 經 analyzer 分詞 | 全文檢索(像倒排索引) | title、description |
把不參與搜尋的欄位也 map 進去 → 每個 index 記憶體開銷變大、成本上升。只 map 你會搜的欄位。
nested 的正規化取捨
把 reviews 巢狀在書的 document 裡(nested 型別)vs 另建 reviews index——和 RDBMS 的正規化/反正規化一樣:
- 評論少改、常被一起查 → 巢狀(省一次 join)
- 評論常更新 → 另建 index(避免每次改評論都重寫整本書 document)
基本操作(REST API)
PUT /books 建立 index(可設 shards / replicas)
PUT /books/_mapping 設定 mapping
POST /books/_doc 新增 document(回傳 _id、_version)
PUT /books/_doc/{id} 整份覆寫
POST /books/_update/{id} 只更新部分欄位
_version = 樂觀並行控制
ES 用 _version 做 document 的原子更新。PUT /books/_doc/{id}?version=1 表示「只有 version 為 1 時才更新」,否則回錯 → client 處理衝突並重試。
注意 ES 是分散式、非同步、並行的,request 可能到多個 node 且順序不定——更新語意要寫清楚。
搜尋查詢
語法接近 SQL 思維,用 JSON 表示、好組合:
match → 全文比對單一欄位
bool → must / should / must_not 組合多條件
range → 數值/日期範圍(price lte 1000)
nested → 在巢狀物件內搜(reviews.comment + reviews.rating)
回應帶 _score(相關度分數)、_source(原始 JSON)。
排序的四種方式
| 方式 | 機制 |
|---|---|
| 欄位排序 | sort: [{price: asc}],可多欄位 |
| Script 排序 | Painless script 依計算值排序(如打折後價格) |
| Nested 排序 | 排序值取自同一巢狀物件(mode: max) |
| 相關度 | 不指定 sort 時的預設,_score ≈ TF-IDF |
分頁:三種策略
| 策略 | 機制 | 代價 |
|---|---|---|
| from / size | 指定起始位置 + 每頁筆數 | 深分頁(> 1 萬)極貴,要先排序取出前面所有 doc |
| search_after | 用「上一頁最後一筆的 sort 值」當下一頁起點 | 不能隨機跳頁、只能往後;不漏不重 |
| PIT cursor | Point in Time + search_after | 成本最高,但提供一致視角 |
from/size: 每頁都重排 + 取出前面全部 → 深分頁雪崩
search_after: 只取「上一頁最後一筆之後」 → 逐步縮小範圍,省資源
PIT + after: 凍結一個時間點的快照 → 翻頁期間資料在變也不影響
即使兩次請求間有新增 document 也不會漏、不會在頁間重複;代價是 client 要自己記住「上一頁最後一筆的 sort 值」。
底層架構
Cluster 與五種 Node
| Node 型別 | 職責 |
|---|---|
| Master | 協調 cluster,唯一能建/刪 index、增刪 node(管理員) |
| Data | 實際存資料;搜尋真正發生的地方 |
| Coordinating | 對外入口,解析查詢、分發到 shard、合併結果 |
| Ingest | 寫入前置處理(ingest pipeline) |
| Machine Learning | ML 相關工作 |
一個 instance 可同時扮演多種角色。Master-eligible nodes 跑 leader election,同時只有一個 active master。
寫入路由:hash(_id) % number_of_shards 決定寫進哪個 primary shard。
Shard 與 Replica
- Shard:把資料 + 對應 index 切開分散到多台主機;搜尋並行跑在所有相關 shard,由 coordinating node 合併排序。
- Replica:某 shard 的完整拷貝。用途:高可用 + 提高吞吐——若一個 shard 能處理 X TPS、有 Y 個 replica,理論上可到 X × Y TPS。
- ES 的 shard 與 Lucene index 一對一。
Lucene Segment 的不可變 CRUD
寫入先批次累積 → 建好一個 segment → flush 到磁碟。segment 太多時 merge 成新的再刪舊的。
| 操作 | 做法 |
|---|---|
| 新增 | 寫進新 segment,不動舊的 → 寫入快 |
| 刪除 | 標記「已刪除」ID,查詢時當不存在;merge 時才真正清掉 → 刪除快 |
| 更新 | 對舊 document 做 soft delete + 插入一筆新的 → merge 時清理 |
更新比插入更耗效能(要處理 soft delete 簿記)。merge 前會一直帶著效能包袱。「按讚數/曝光數」這類高頻更新讓 ES 很吃力。
不可變設計換來:寫入快、快取簡單(segment 不變可安心 cache)、並行單純、壓縮效果好。
Segment 內的兩個關鍵結構
| 結構 | 解決什麼 | 機制 |
|---|---|---|
| Inverted Index | 「哪些 doc 符合」 | 詞 → document 清單;把 O(n) 掃描變 O(1) 查表(Lucene 的心臟) |
| Doc Values | 排序 / aggregation | 單一欄位、整個 segment 的欄式(columnar)連續儲存 |
Query Planning
Coordinating node 的 query planner 用統計資訊(欄位型別、詞分布、document 長度)選最省的執行策略。
例:搜 "bill nye",bill 有幾百萬筆、nye 只有幾百筆 → 先建 nye 集合再掃 bill 比反過來快好幾個數量級。
面試用法與限制
題目涉及複雜搜尋(排序、篩選、排名、分面)通常都是好候選。實務上常用 CDC 從 Postgres / DynamoDB 同步權威資料進 ES。
幾條一定要會講的限制:
- 別當唯一資料庫:它首先是搜尋引擎,一致性/持久性設計目標不是取代 DB。
- 讀多寫少:寫入很重要加一層 write buffer 或換方案。
- 最終一致:搜尋結果可能落後,無法接受就找其他方案。
- 反正規化:不是關聯式 DB,資料要攤平讓一兩次查詢就拿到結果。
- 不是所有搜尋都需要 ES:資料量小(< 10 萬)或很少變動,先試主 DB 的簡單查詢(甚至 Postgres 的 GIN 全文索引)。
- 同步一致性:ES 與底層資料同步失敗 → 資料漂移,是常見 bug 來源。
可遷移的設計原則(即使不用 ES)
- 在合適層級用不可變性:更容易快取、壓縮、優化,省掉可變資料的同步難題。
- 查詢執行與資料儲存分離:Data node vs Coordinating node 各自優化。
- 索引策略直接決定效能:inverted index 讓全文檢索快、doc values 讓排序/聚合有效率——依常見查詢模式設計索引結構。
Related Notes
- 08-Search-and-AI/01-Search-System — 搜尋系統的整體設計(CDC pipeline、BM25、Reindex alias)
- 04-Database-Indexing/06-Inverted-Index — inverted index 的索引原理
- 08-Search-and-AI/04-Vector-Database — ES kNN 也支援向量 + BM25 hybrid search
- 09-Messaging-Coord/01-Data-Pipeline — CDC 把權威資料同步進 ES
- 06-Database-Tech/02-PostgreSQL — 小規模搜尋先用 Postgres GIN 全文索引,別急著上 ES
- 02-Distributed-Systems/02-CAP-Theorem — ES 的最終一致性取捨
- 08-Search-and-AI/Practice-Search-and-AI