Elasticsearch 正式上線、營運與效能優化 — Production 生存指南
你蓋了一棟房子。牆壁漂亮、格局合理、水電也通了。然後你搬進去住,第一個冬天就發現:暖氣不夠、水管會凍裂、電線負載不了所有家電同時開。
蓋房子跟住房子是兩回事。寫 Elasticsearch 跟跑 Elasticsearch 也是。
這篇是 Elasticsearch 系列的最後一塊拼圖——把你在開發機上「能跑」的東西,變成在 Production 上「跑得穩、跑得快、掛不了」的東西。
兩個設定檔決定生死
elasticsearch.yml——叢集的身分證
1 | cluster.name: my-production-cluster |
幾個踩坑點:
cluster.name 絕對不要用預設的 elasticsearch。同一網段內如果有另一台也叫 elasticsearch,它們會自動組成叢集——然後你的 Production 資料就跟測試環境混在一起了。
node.roles 是 ES 8.x 的新寫法,取代了舊的 node.master: true 布林值。可以組合:master、data_content、data_hot、data_warm、ingest、ml。角色越精確,資源分配越乾淨。
cluster.initial_master_nodes 只在叢集第一次啟動時有用。組建完成後可以拿掉——留著也沒事,但如果未來加新節點,別忘了它不會自動更新。
jvm.options——記憶體是場零和遊戲
1 | -Xms16g |
四條鐵律:
Xms 必須等於 Xmx。 不等的話 JVM 會動態調整 Heap 大小,每次調整都會觸發 Full GC——你的叢集會突然卡個幾百毫秒到幾秒。
不超過實體記憶體的 50%。 另一半留給作業系統的 Page Cache。Lucene 的 Segment 檔案大量依賴 mmap 和 Page Cache 加速讀取。如果 Heap 佔掉 80%,Page Cache 所剩無幾,Lucene 每次搜尋都得讀磁碟——效能直接腰斬。
不超過 32GB。 超過這個數字,JVM 會關閉 Compressed OOPs 優化,物件指標從 4 byte 變 8 byte。實測結果:Heap 設 64GB 的效能可能還不如 31GB。經典的「多即是少」。
理想值 26–31GB。 具體上限跟 JVM 版本有關,用 -XX:+PrintFlagsFinal 檢查 UseCompressedOops 是否為 true 來確認。
32GB 記憶體的機器 → Heap 設 16GB → Page Cache 可用約 16GB → Segment 檔案住在記憶體中 → 搜尋飛快。
系統層設定——被忽略但致命
1 | # /etc/sysctl.conf |
vm.max_map_count 預設值太低,Lucene 用 mmap 開 Segment 檔案會報錯。262144 是官方建議的最低值。
vm.swappiness=1 幾乎禁用 swap。ES 痛恨 swap——一旦 Heap 被 swap 到磁碟,GC 時間從毫秒級變成秒級。配合 elasticsearch.yml 裡的 bootstrap.memory_lock: true,直接鎖住 JVM Heap 不被 swap。
有人真的踩過這個坑:沒設 memory_lock,某天凌晨 GC 停頓飆到 30 秒。其他 Node 以為這台死了,開始重新分配 Shard。等 GC 結束它又活過來了,但 Shard 已經在搬——叢集雪崩。
Production 上線清單
把開發機搬上 Production 之前,逐項確認。少一項都可能讓你半夜被叫起來。
叢集架構
至少 3 個 Master-eligible Node。 2 個 Node 掛 1 個就沒有多數派(quorum),叢集直接不可用。3 個可以容忍掛 1 個。
Master 和 Data 分離。 Master Node 只管叢集狀態(metadata、Shard 分配),不存資料。如果 Data Node 被大量寫入或搜尋壓到喘不過氣,連帶影響 Master 穩定性——最壞情況是 Master 選舉超時,叢集癱瘓。
Coordinating-only Node 擋在前面。 純 coordinating node 接收查詢、分發到 Data Node、合併結果。它的 Heap 用來做結果合併和排序,不跟 Data Node 搶資源。
硬體
SSD 是必須的。 機械磁碟的隨機讀寫速度比 SSD 慢 100 倍。ES 的 Segment merge、搜尋、indexing 全都仰賴隨機 I/O。機械磁碟在 Production ES 上是災難等級的。
記憶體公式: Heap(不超過 50% 且不超過 31GB)+ 足夠的 Page Cache。64GB 記憶體配 31GB Heap 是常見配置。
安全性
ES 8.x 預設開啟 TLS/SSL,不用額外設定。但 RBAC 要自己配——不要所有人都用 elastic 超級帳號。依角色給不同權限:開發者只能讀自己的 index,DBA 可以管 ILM,SRE 可以看 cluster health。
Audit Log 也建議打開。「誰在什麼時間 DELETE 了哪個 index」——沒有 audit log 你永遠查不出來。
資料保護
Snapshot 備份到 S3/GCS/Azure Blob。 設定排程,每天至少一次。有人在 Production 執行 DELETE /orders,一年的訂單資料消失——沒有 Snapshot 就是沒有。
Replica 至少 1。 確保單一 Node 掛掉不丟資料。寫入密集期間可以暫時設 0(效能提升 30–50%),完成後立刻改回。
ILM 生命週期管理。 自動 hot → warm → cold → delete。不設 ILM,磁碟終究會滿。ES 在磁碟用量超過 85% 時停止分配新 Shard,超過 95% 會把所有 index 設為唯讀。凌晨三點收到「所有索引變唯讀」的告警——原因就是磁碟滿了。
寫入效能——從「能寫」到「瘋狂寫」
API 層
永遠用 _bulk。 單筆寫入 = 1000 倍 HTTP overhead。一次 1000–5000 筆,payload 控制在 5–15 MB。多執行緒並行(2–4 條),單執行緒打不滿 ES 的寫入頻寬。
Index Settings
大量匯入時的三板斧:
refresh_interval改成30s或-1(關閉自動 refresh)。減少 Segment 產生頻率,效能提升 30–50%。完成後改回1s。number_of_replicas設0。寫入不用等 Replica 同步。完成後改回 1+。translog.durability改成async。每 5 秒 fsync 一次而不是每次請求都 fsync。日誌場景可以接受微量丟失,提升 20–30%。
Mapping 層
不搜尋的欄位設 "index": false——備註、電話號碼這種,不建 Inverted Index,省空間省 CPU。
不排序不聚合的欄位設 "doc_values": false——keyword 和 numeric 預設有 doc_values,確定不需要就關掉。
避免 Dynamic Mapping。用 "dynamic": "strict",新欄位必須手動加。不然某天某個 client 多送了一個 debug_info 欄位,ES 自動幫你建 mapping、建 index——莫名其妙多了一堆垃圾欄位吃資源。
搜尋效能——200ms 內回應
電商搜尋超過 200ms 使用者就感到遲鈍,超過 1 秒就會離開。
最高 ROI:精確過濾放 filter 不放 must。 不算分 + 可快取,快 2–5 倍。(模組七講過,這裡再強調一次——因為真的太重要了。)
控制 _source 回傳。 只取需要的欄位。一筆文件如果有 10KB 的 description,搜 100 筆就是 1MB 的傳輸。用 "_source": ["name", "price", "brand"] 過濾到只剩你要的。
控制 Segment 數量。 Segment 越少搜尋越快。唯讀的歷史索引跑一次 forcemerge 合併到 1 個 Segment,速度快 2–3 倍。活躍索引靠背景 Merge 處理。
Shard 大小控制在 10–50 GB。 太小(1 GB × 1000 個 Shard)的 overhead 浪費在 metadata 和 coordinating 上。太大(200 GB × 2 個 Shard)搜尋慢又不好恢復。
深分頁用 search_after。 from + size 超過 10,000 會崩。search_after 效能恆定——不管翻到第幾頁都一樣快。
監控——你不盯它,它就盯你
1 | GET _cluster/health |
四個指令夠你做基本健檢。
Cluster Health: green 正常,yellow 有 Replica Shard 未分配(通常是單節點開發環境),red 有 Primary Shard 遺失——要立刻處理。
JVM Heap 使用率: 持續超過 85% 就該擴容或優化查詢了。GC 頻繁 = 效能直線下降。
Search Latency / Indexing Rate: 建立 baseline,偏離超過 30% 觸發告警。不知道「正常」長什麼樣子,就不可能知道「異常」在哪裡。
慢查詢日誌也建議打開:
1 | PUT /my_index/_settings |
超過 5 秒的搜尋、超過 2 秒的 fetch、超過 5 秒的 indexing——自動記到 slowlog 裡。除錯的時候不用猜,直接去看 log。
經典災難案例
Heap 設 64GB 效能反而差。 超過 32GB 失去 Compressed OOPs,物件指標膨脹。64GB 的可用效能可能還不如 31GB。
沒設 memory_lock,GC 停頓 30 秒。 Heap 被 swap 到磁碟,GC 暫停從 50ms 飆到 30 秒。其他 Node 以為它死了,開始搬 Shard,叢集雪崩。
磁碟超過 85% 停止寫入。 凌晨三點告警:所有索引變唯讀。ES 的自我保護機制觸發了 cluster.routing.allocation.disk.watermark.high。
誤刪 Index 沒有備份。 有人在 Production 執行 DELETE /orders。沒有 Snapshot。一年訂單資料消失。
升級沒用 Rolling Upgrade。 直接停掉整個叢集升級,數小時停機。正確做法:一個 Node 一個 Node 滾動升級,叢集全程可用。
每一個案例背後都是同一個教訓——Production 環境的問題 99% 不是 ES 的 bug,是設定沒到位。
從模組一的 Hello World 到模組八的 Production 部署,這整條路的核心其實就兩件事:理解資料怎麼儲存的,跟理解查詢怎麼運作的。
Inverted Index 讓你知道搜尋為什麼快。Segment 和 Translog 讓你知道寫入為什麼安全。BM25 讓你知道排名怎麼來的。Shard 和 Replica 讓你知道資料怎麼分散。JVM Heap 和 Page Cache 讓你知道記憶體怎麼分配。
把這些拼起來,你就不是在「用 ES」——你是在「理解 ES」。
遇到效能問題的時候,你不會去 Stack Overflow 找答案。你會知道該看哪個層——是 Query 層的 filter 沒用對?是 Segment 太多沒 merge?是 Heap 被 field data 吃掉了?是 Shard 分配不均?
那才是這八個模組真正要教的東西。
本文為 Elasticsearch 系列課程模組八筆記改寫










