一、索引的動態設置、靜態設置
- 索引設置包含兩部分核心內容:
- 靜態設置(static index settings),只允許在創建索引時或者針對已關閉的索引進行設置。
- 指動態設置(dynamic index settings),可以借助更新設置(update settings)的方式進行動態更新,更新后立即生效。
1. 靜態設置
靜態設置實戰場景舉例如下:
設置主分片大小的參數是index.number_of_shards,只在創建索引時生效,不支持動態修改。
默認主分片大小為1,且每個索引的分片數量上限默認為1024。此限制是一個安全限制,可防止索引分片數過多導致集群不穩定。
如果在業務層面擴充節點后確實需要擴展主分片數,該怎么辦?
答案:在非業務核心時間通過reindex操作遷移實現。?
2. 動態設置
動態設置的實戰場景舉例如下:
- 設置副本數參數為index.number_of_replicas,可以動態修改:
PUT news_index/_settings
{"number_of_replicas":3
}
- 設置刷新頻率參數為index.refresh_interval,可以動態修改:
PUT news_index/_settings
{"refresh_interval":"1s"
}
默認刷新頻率參數值為1s,即每秒刷新一次。這1s決定了Elasticsearch是近實時的搜索引擎,而非準實時搜索引擎。如果業務層面對實時性的要求不高,可以考慮將該值調大。因為如果采用1s,則每秒都會生成一個新的分段(關于分段的概念可以參考最后一章),會影響寫入性能。
- max_result_window是Elasticsearch中的一個設置參數,用于控制搜索結果的最大窗口的大小:
默認情況下,max_result_window的值為10000,這意味著在分頁搜索時最多可以返回10000條數據。如果每頁可顯示10條數據,那么最多可以翻到1000頁。在某些情況下,可能需要處理比默認值更大的數據集。
在這種情況下,可以通過更新索引設置來動態修改max_result_window的值:
PUT news_index/_settings
{"max_result_window":50000
}
上述命令將max_result_window的值設置為50000。此時如果每頁顯示10條數據,則可以最多翻到5000頁。
增大max_result_window的值可能會對Elasticsearch集群的性能產生影響,尤其是在處理大量數據時。因此,在根據實際需求調整此參數時,要權衡性能和查詢范圍之間的關系。如果需要遍歷大量數據,則建議使用scroll API或search_after參數,以更高效地進行處理。
在 Elasticsearch 中,max_result_window參數(默認值為 10000)主要限制的是基于from + size的深度分頁查詢(即通過from指定偏移量,size指定每頁大小)。這種查詢方式在偏移量(from)較大時,會導致 Elasticsearch 在每個分片上生成大量中間結果并合并,消耗大量內存和 CPU,甚至引發 OOM(內存溢出)。
而 scroll API 和 search_after 這兩種分頁方式,設計上避免了max_result_window的直接限制。
二、索引別名
- 索引別名常見的使用場景:
- 當需要定期創建新索引(如日志按天 / 月分割),同時保持應用端無需感知索引名稱變化時,通過別名指向 “當前有效索引”,實現無縫切換。
- 索引重建或結構升級:舊索引 users_v1 需升級到 users_v2(如新增字段、調整映射),先通過別名 users 指向 users_v1,應用正常訪問,重建完成后,通過原子操作切換別名指向 users_v2,實現無感知遷移。
索引別名只是物理索引的軟鏈接的名稱而已,一個索引可以創建多個別名,一個別名也可以指向多個索引。
實戰中,很多工程師在開發中后期才發現索引別名的妙處。正如前文所說,別名能進行高效的索引管理,能進行索引數據修改或更新操作并確保用戶無感知。
-
示例場景:
線上索引users_v1
需要新增一個分詞器為ik_max_word
的字段address
,但直接修改映射會導致集群分片重建,且舊數據無法應用新分詞器(需重建索引)。 -
傳統方案(無別名)的痛點:
- 重建新索引
users_v2
并遷移數據,需修改所有客戶端代碼/配置中的索引名,易遺漏導致線上故障; - 切換期間需停機或雙寫,用戶體驗差。
- 別名實現無感知升級(步驟):
-
創建新索引并綁定臨時別名:
PUT /users_v2 {"mappings": {"properties": {"address": { "type": "text", "analyzer": "ik_max_word" }}} } POST /_aliases {"actions": [{ "add": { "index": "users_v2", "alias": "users_tmp" } }] }
- 開發/測試環境通過
users_tmp
驗證新索引邏輯,不影響線上users_v1
。
- 開發/測試環境通過
-
生產環境雙寫驗證:
- 應用端同時寫入
users_v1
和users_v2
(通過別名解耦,代碼無需硬編碼索引名), - 讀取時通過別名
users
暫指向users_v1
,確保線上流量無影響。
- 應用端同時寫入
-
原子切換別名指向:
POST /_aliases {"actions": [{ "remove": { "index": "users_v1", "alias": "users" } },{ "add": { "index": "users_v2", "alias": "users" } }// 可同時刪除舊索引(需確保數據遷移完成)] }
- 切換瞬間完成,客戶端無感知,無需重啟服務或修改配置。
- 核心價值:
- 風險隔離:通過臨時別名
users_tmp
驗證新索引,避免直接操作線上索引; - 零停機遷移:利用別名的原子操作,實現“熱切換”,用戶請求始終路由到有效索引。
- 從別名檢索:
在 Elasticsearch 中,檢索時使用索引別名與使用真實索引名的操作完全一致,別名會被透明解析為實際指向的索引(單個或多個)。
# 簡單查詢
GET /my_alias/_search
{"query": { "match_all": {} }
}# 帶過濾的查詢
GET /my_alias/_search
{"query": { "term": { "status": "active" } }
}
若別名指向多個索引(如 logs_2025_q1 指向 logs-2025-01、logs-2025-02、logs-2025-03),檢索時會同時查詢所有關聯索引:
GET /logs_2025_q1/_search
{"query": { "range": { "timestamp": { "gte": "2025-01-01" } } }
}
等價于 GET /logs-2025-01,logs-2025-02,logs-2025-03/_search,但別名簡化了索引列表的維護。
別名支持通配符模式(如 logs-*),檢索時自動匹配所有符合模式的索引:
# 創建別名匹配2025年4月的所有日志索引
POST /_aliases
{"actions": [{ "add": { "index": "logs-2025-04*", "alias": "april_logs" } }]
}
# 檢索時使用別名
GET /april_logs/_search
{ "query": { "term": { "service": "user-center" } } }
避免在 DSL 中硬編碼索引模式(如 logs-2025-04*),通過別名統一管理匹配規則。
三、索引模板
- 兩個常見的業務場景問題:
- 問題1:數據量非常大,需要進行索引生命周期管理,具體要按日期劃分索引,且要求多個索引的Mapping一致,而每次手動創建或者腳本創建都很麻煩,怎么辦?
- 問題2:實際業務中應用了多個索引,想讓這些索引中相同名字的字段類型完全一致,以便實現跨索引檢索,怎么辦?
我們會發現傳統方式不能解決多索引的快速定義和高效管理等問題。因此,索引模板應運而生。
1. 索引模板的定義
Elasticsearch 7.8及之后版本支持兩種定義模板的方式,可簡記為普通模板定義方式和組件模板新增/創建方式。
- 普通模板定義方式如下所示:
PUT _index_template/<template_name> # 模板名稱(唯一)
{"index_patterns": ["logs-*", "metrics-*"], # 匹配的索引名模式(支持通配符)"priority": 100, # 模板優先級(高優先級覆蓋低優先級)"template": { # 新索引的配置內容"settings": { # 索引設置(分片、副本、刷新間隔等)"number_of_shards": 3,"number_of_replicas": 1,"refresh_interval": "30s"},"mappings": { # 字段映射(類型、分詞器、動態模板等)"dynamic": "strict", # 嚴格模式(禁止自動添加未定義字段)"properties": {"@timestamp": { "type": "date" }, # 時間字段(必選,用于時序數據)"message": { "type": "text", "analyzer": "ik_max_word", # 中文分詞器(需提前安裝)"fields": { "keyword": { "type": "keyword" } } # 同時存儲keyword子字段}}},"aliases": { # 為新索引自動綁定別名"current_logs": {} # 別名指向新索引(無額外配置)}},"composed_of": ["ilm_policy_template"] # 組合其他組件模板(可選,8.x+ 支持)
}
而組件模板的核心在于將原有普通模板定義的mappings、settings等以組件的方式分隔,以便最小化更新模板。
- 組件模板定義方式如下所示:
由上可知,模板名稱為mydata_template,包含兩個核心組件——component_mapping_template、component_settings_template。component_mapping_template組件模板實現了映射的定義,component_settings_template實現了設置和別名的定義。
當業務層面需要更新映射時,只需要更新component_mapping_template組件模板即可,改動范圍更小、操作更精細化。
2. 索引模板應用的常見問題
模板和索引在應用上的區別是什么?
索引針對的是單一索引,類似MySQL中的一個表。而模板針對一個或多個索引,或者說是針對具有相同表結構的一類索引。
如果想更新映射,那么可以通過更新模板來實現嗎?
首先需要建立這樣一個認知前提:一旦創建了映射,除幾個特定的類型以外,其他類型都不支持更新,除非進行reindex操作。
所以,一旦創建了索引,對索引模板的更新將不會影響該索引。
更新模板僅適用于新創建的索引。更新為動態模板僅會影響索引中的新字段。
附
分段
在Elasticsearch(基于Lucene實現)中,“分段”(Segment)是底層存儲和處理數據的基本單元,本質上是一個不可變的倒排索引文件。以下是具體解釋:
分段的本質與作用
- 倒排索引載體:每個分段存儲一組文檔的倒排索引(關鍵詞到文檔的映射),是Lucene實現快速搜索的核心數據結構。
- 獨立搜索單元:分段一旦生成就不可修改,可獨立被搜索,多個分段的搜索結果會在查詢時合并。
- 寫入過程的中間產物:文檔寫入時不會直接寫入磁盤上的主索引,而是先存入內存緩沖區,通過定期刷新(
refresh
)生成新分段。
分段與“近實時”機制的關系
-
默認1秒刷新(
refresh_interval
):
Elasticsearch默認每1秒將內存緩沖區中的文檔寫入一個新分段(并開放搜索),這使得數據在寫入后1秒內可見,實現“近實時”(Near Real-Time)。- 若調大該值(如
30s
),則每30秒生成一個分段,數據可見延遲增加,但減少分段生成頻率。
- 若調大該值(如
-
“準實時”與“近實時”的區別:
- 準實時:數據可見延遲較長(如分鐘級),分段生成頻率低。
- 近實時:通過高頻刷新(1秒)縮短延遲,但代價是分段數量增加。
分段對寫入性能的影響
-
頻繁生成分段的代價:
每次refresh
會:- 將內存數據寫入分段文件(磁盤I/O);
- 生成新的分段元數據(如文件句柄、索引結構);
- 可能觸發后續的分段合并(
merge
)操作(長期分段過多時,Elasticsearch會自動合并小分段為大分段,減少搜索時的開銷)。
這些操作在高寫入負載下會消耗CPU、磁盤I/O和內存資源,降低寫入吞吐量。
-
調大刷新間隔的優勢:
減少分段生成頻率,降低I/O和元數據管理開銷,提升寫入性能,適合對實時性要求不高的場景(如日志分析、離線報表)。
分段的生命周期
- 生成:通過
refresh
操作將內存數據寫入新分段(默認1秒一次)。 - 存在:分段不可變,可被搜索,直到被合并或刪除。
- 合并:Elasticsearch后臺定期合并小分段為大分段,減少分段數量,提升搜索效率(合并過程會釋放舊分段資源)。
- 刪除:當文檔被刪除時,分段不會立即修改,而是記錄“刪除標記”,合并時才真正移除被刪除的文檔。