Elasticsearch 8.X 檢索實(shí)戰(zhàn)調(diào)優(yōu)錦囊 001
檢索錦囊 1:盡可能的使用緩存
對(duì)于精準(zhǔn)匹配的查詢,不關(guān)注評(píng)分結(jié)果,只關(guān)注數(shù)據(jù)是否滿足檢索需求。
可以考慮用 filter “包裹一層”,如處理時(shí)間范圍檢索,Elasticsearch 能緩存部分結(jié)果。但,要說明的是更換時(shí)間窗口,換不同時(shí)間段檢索,原有緩存不起作用。
關(guān)于 filter 的緩存效果,官方文檔如是說:
“Frequently used filters will be cached automatically by Elasticsearch, to speed up performance.”
即:“頻繁的使用過濾器會(huì)有自動(dòng)緩存的“效果”,以提高性能。”
舉例如下,“/m”的本質(zhì)使檢索不是具體到某秒的精確值,而是擴(kuò)展到分鐘。
GET kibana_sample_data_flights/_search
{
"profile": true,
"query": {
"constant_score": {
"filter": {
"range": {
"timestamp": {
"gte": "now-1h/m",
"lte": "now/m"
}
}
}
}
}
}
加“profile;true”后,看到的檢索結(jié)果如下。

官方文檔解釋的不夠準(zhǔn)確,這里結(jié)合 profile:true的結(jié)果,解釋一下。
起始時(shí)間:now-1h/m 為當(dāng)前時(shí)間戳減去1分鐘所在分鐘的 00 秒的時(shí)刻; 結(jié)束時(shí)間:now為當(dāng)前時(shí)間戳所在分鐘的 59秒的時(shí)刻。
更為確切的說,時(shí)間跨度為 2 分鐘了。

https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-search-speed.html#_search_rounded_dates
檢索錦囊 2:文件系統(tǒng)緩存預(yù)熱
如果 Elasticsearch 節(jié)點(diǎn)重啟,文件系統(tǒng)緩存通常是空的。
操作系統(tǒng)需要一些時(shí)間才能將索引的熱數(shù)據(jù)加載到內(nèi)存中,以便快速檢索。
我們可以通過如下的設(shè)置來(lái)告知操作系統(tǒng)哪些文件應(yīng)立即加載到內(nèi)存中。
PUT /my-index-000001
{
"settings": {
"index.store.preload": ["nvd", "dvd"]
}
}
支持的文件擴(kuò)展名及釋義如下所示。
| 擴(kuò)展名 | 英文釋義 | 詳細(xì)釋義 |
|---|---|---|
| .nvd | Norms data | 查詢時(shí)使用的各種歸一化因子數(shù)據(jù) |
| .dvd | doc values Per-Document Values | 用于聚合、排序的正排索引文件 |
| .tim | terms dictionaries | 單詞詞典 |
| .doc | Frequencies | 倒排列表,包含:文檔列表及詞頻 |
| .dim | points | Point values |
注意:index.store.preload 設(shè)置為“*”通常沒有意義,因?yàn)樗形募虞d到內(nèi)存中通常沒有用。
而更好的選擇可能是將其設(shè)置為 ["nvd", "dvd", "tim", "doc", "dim"],也就是包含 Norms data、docvalues、單詞字典、倒排索引列表等,這些是搜索和聚合中最重要的部分。
https://lucene.apache.org/core/8_0_0/core/org/apache/lucene/codecs/lucene80/package-summary.html
https://www.shenyanchao.cn/blog/2018/12/04/lucene-index-files/
檢索錦囊 3:使用預(yù)過濾分片執(zhí)行檢索
大背景是:對(duì)于時(shí)序數(shù)據(jù),可以使用 ilm 索引生命周期管理,ilm 索引生命周期管理的前置條件是冷熱集群架構(gòu)。也就是:我們有 rollover 滾動(dòng)索引機(jī)制,可以設(shè)置索引在熱節(jié)點(diǎn)、溫節(jié)點(diǎn)、冷節(jié)點(diǎn)的生存時(shí)長(zhǎng)。
進(jìn)一步說,索引不是普通的索引,索引有了時(shí)間戳的后綴。這樣的好處是:當(dāng)我們需要檢索數(shù)據(jù)的時(shí)候,是可以通過別名等方式物理縮小索引范圍區(qū)間的。
舉個(gè)例子:如下圖所示,weibo_2527 實(shí)際指的是上面的“20190225,20190226,20190227”三個(gè)索引,如果只檢索這三天的數(shù)據(jù),相比于全量數(shù)據(jù),weibo_2527別名意味著極大的降低了檢索數(shù)據(jù)樣本空間。

但,索引層面還足夠大,看上面截圖我們知道,每個(gè)索引下面又有 N 多分片。能否繼續(xù)優(yōu)化,下沉到分片層面進(jìn)行快速鎖定分片執(zhí)行高效檢索呢?
這就用到了 7.4+ 版本才有的新特性:prefilter shard。使用 prefilter shard,Elasticsearch 能夠根據(jù)我們的請(qǐng)求確定需要查詢的分片。
預(yù)處理分片的本質(zhì)如張超老師所講:“對(duì)于 Date 類型的 Range 查詢,在對(duì)分片執(zhí)行搜索之前,先檢查一下分片是否包括被查詢的數(shù)據(jù)范圍,如果查詢的范圍與分片持有的數(shù)據(jù)沒有交集,就跳過該分片。”本質(zhì)一句話:有助于避免查詢到達(dá)不必要的分片。
默認(rèn)情況下,此預(yù)過濾分片階段在以下情況下執(zhí)行:
條件一:該請(qǐng)求針對(duì)超過 128 個(gè)分片。 條件二:請(qǐng)求針對(duì)一個(gè)或多個(gè)只讀索引。 條件三:基于創(chuàng)建過索引的字段進(jìn)行排序。
這點(diǎn),參考張超老師驗(yàn)證且給出的結(jié)論——“pre-filter 最主要的作用不是降低查詢延遲,而是 pre-filter 階段可以不占用 search theadpool(檢索線程池),相比于不加這個(gè)參數(shù),會(huì)減少了檢索線程池的占用情況?!?/span>
具體使用方式如下所示。
相當(dāng)于在原來(lái)檢索的基礎(chǔ)上加了:pre_filter_shard_size參數(shù)。
POST kibana_sample_data_flights_20220727/_search?pre_filter_shard_size=1000
{
"query": {
"range": {
"timestamp": {
"gte": "2022-07-01",
"lte":"2022-07-31"
}
}
}
}
https://easyice.cn/archives/350
檢索錦囊 4:合并只讀分片
我們發(fā)現(xiàn),除了借助 rollover (ilm 索引生命周期管理)將冷數(shù)據(jù)索引標(biāo)記為只讀之外,我們還可以強(qiáng)制合并(force merge)一個(gè)或多個(gè)索引的分片。與磁盤碎片整理類似,此操作在不涉及緩存時(shí)可極大地提高了查詢性能。經(jīng)過只讀分片的合并,最大響應(yīng)時(shí)間由 30 秒降到了2 秒。
POST /.ds-my-data-stream-2099.03.07-000001/_forcemerge?max_num_segments=1&pretty
https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-forcemerge.html#forcemerge-api-time-based-index-ex
檢索錦囊 5:新建索引時(shí)配置排序方式
在 Elasticsearch 中創(chuàng)建新索引時(shí),可以配置指定每個(gè) Shard 中的 Segments 的排序方式。
默認(rèn)情況下,Lucene 不應(yīng)用任何排序。index.sort.* 設(shè)置定義了應(yīng)該使用哪些字段來(lái)對(duì)每個(gè)段內(nèi)的文檔進(jìn)行排序。
舉例:如下索引的定義中(篇幅原因,省略了 Mapping),指定了段內(nèi)基于 timestamp 字段進(jìn)降序排序。
PUT kibana_sample_data_flights_20220727
{
"settings": {
"index": {
"sort.field": "timestamp",
"sort.order": "desc",
"number_of_shards":10,
"number_of_replicas":0
}
},
"mappings": {
"properties": {
....
}
}
}
}
同時(shí),有些業(yè)務(wù)場(chǎng)景,用戶不真正關(guān)心跟蹤命中的總數(shù),并且只希望查詢的Top N 個(gè)結(jié)果。這時(shí)候可以基于“提前終止查詢”來(lái)快速獲取檢索結(jié)果。那么如何做到提前終止查詢呢?
我們都知道:Elasticsearch 默認(rèn)會(huì)在 query 階段查詢每個(gè)文檔,基于給定條件排序后,然后在 fetch 階段取滿足排序條件的結(jié)果數(shù)據(jù)并返回給客戶端。
這就意味著分段數(shù)越多,排序自然也會(huì)越慢,查詢的時(shí)間越久。
提前終止查詢的前置條件是:寫入的時(shí)候,已經(jīng)基于字段排序了。假設(shè)我們最終期望返回 Top 10 數(shù)據(jù),每個(gè)分段內(nèi)各自取 Top 10 然后再整體排序得到 Top 10,不就可以了嗎?
打個(gè)不恰當(dāng)?shù)念惐龋澜绫枰獩Q出前 10 名,那么:亞洲取前10,歐洲取前10,非洲取前10,美洲取前10,整體排序不就是世界足球 Top10了嗎?

原理明白了,問題就轉(zhuǎn)化為:如何提前終止呢?
實(shí)際上并沒有特殊參數(shù)控制,因?yàn)槲覀兦爸迷O(shè)置了:"sort.field": "timestamp", Elasticsearch 會(huì)根據(jù) size 大小每個(gè)分段取 Top 10 數(shù)據(jù)后自動(dòng)終止。
`"track_total_hits": false`的目的是不顯示文檔總數(shù),這也能降低檢索時(shí)間,提高檢索效率。
執(zhí)行操作如下所示。
POST _reindex
{
"source": {
"index": "kibana_sample_data_flights"
},
"dest": {
"index": "kibana_sample_data_flights_20220727"
}
}
POST kibana_sample_data_flights_20220727/_search
{
"size": 10,
"sort": [
{
"timestamp": "desc"
}
],
"track_total_hits": false
}
https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-index-sorting.html#early-terminate
6、小結(jié)
在查詢中使用近似日期(now/m)而非精準(zhǔn)日期格式,以利用節(jié)點(diǎn)查詢緩存。 時(shí)序數(shù)據(jù)場(chǎng)景,可以將冷數(shù)據(jù)顯示設(shè)置為只讀來(lái)強(qiáng)制執(zhí)行預(yù)過濾分片機(jī)制。 必要時(shí)對(duì)索引執(zhí)行強(qiáng)制合并(force merge),確保“零碎”的分片合并為一個(gè)大分段,以提高檢索效率。 關(guān)閉超過一年+的索引(具體結(jié)合業(yè)務(wù)需求,如果還在用就不能關(guān)),以減少打開的分片數(shù)量,避免將資源浪費(fèi)到無(wú)用的數(shù)據(jù)上。 借助索引生命周期管理 ILM 管理時(shí)序數(shù)據(jù),實(shí)現(xiàn)索引數(shù)據(jù)的 rollover(滾動(dòng)),設(shè)置只讀、強(qiáng)制合并及索引關(guān)閉任務(wù),而不是手動(dòng)執(zhí)行這種操作。
在“每月存儲(chǔ)大約 新增 500 萬(wàn)+數(shù)據(jù),每天后端接收 2萬(wàn)次查詢請(qǐng)求”的實(shí)戰(zhàn)業(yè)務(wù)場(chǎng)景下,如上的優(yōu)化效果卓著??磳?duì)比效果圖:
| 檢索對(duì)比 | 平均響應(yīng)時(shí)間(秒) | 最長(zhǎng)響應(yīng)時(shí)間(秒) | 最短響應(yīng)時(shí)間(秒) |
|---|---|---|---|
| 優(yōu)化前 | 4.619 | 29.863 | 0.365 |
| 優(yōu)化后 | 0.059 | 1.806 | 0.010 |
如上文章翻譯自:https://medium.com/teads-engineering/practical-elasticsearch-performance-tuning-on-aws-8c08066e598c
我做了細(xì)節(jié)展開解讀。
你的業(yè)務(wù)開發(fā)或運(yùn)維中如何做的檢索優(yōu)化呢?歡迎留言討論交流。
推薦閱讀

