es的term 、terms 和 match、range 和緩存

es 的term 、terms 和 match、range 和緩存

本文是學習es的中文官網,自己的學習總結
整體數據插入及查詢流程

數據存入

將新接收的數據存在buffer中,數據分詞處理後,新建倒排索引 生成segment文件, (正常是在將數據寫入磁盤時生成segment )但爲了實時性, 把這個segment 刷到文件系統緩存中, 此時lucene可以檢索這個新生成的segment, 爲了保證數據的安全性(服務宕機丟失緩存數據), ES在在把數據寫到內存buffer的時候同時還記錄了一個translog日誌。當異常發生時translog日誌文件依然保持原樣。ES會從commit位置開始,恢復整個translog文件中的記錄保持數據一致性。當文件系統的緩存真正同步到磁盤上時,commit文件才更新。Translog才清空。ES默認每5秒或者每次請求操作之前都強制將translog的內容同步到磁盤上。提高了數據的安全性。ES提供了單獨的/_refresh接口,默認將新生成的segment刷到文件系統緩存的時間間隔是1秒,也可以通過參數修改間隔

數據查詢

數據分詞後生成索引,當我們DSL語言最終都會轉換成term 原子操作,然後根據term的結果進行評分

term(精確值查找)

term 查詢, 可以用它處理數字(numbers)、布爾值(Booleans)、日期(dates)以及文本(text)
trem 搜索字符串時 要將字段設置成 not_analyzed 無需分析的。不然es會將字符串進行分詞,分詞結果建立索引,在用trem進行精確查找時找不到任何文檔

//將字段 productID 設置成無需分析的字段。
PUT /my_store 
{
    "mappings" : {
        "products" : {
            "properties" : {
                "productID" : {
                    "type" : "string",
                    "index" : "not_analyzed"  
                }
            }
        }
    }
}
// 初始化查詢
POST /my_store/products/_bulk
{ "index": { "_id": 1 }}
{ "price" : 10, "productID" : "XHDK-A-1293-#fJ3" }
{ "index": { "_id": 2 }}
{ "price" : 20, "productID" : "KDKE-B-9947-#kL5" }
{ "index": { "_id": 3 }}
{ "price" : 30, "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": 4 }}
{ "price" : 30, "productID" : "QQPX-R-3956-#aD8" }

GET /my_store/products/_search
{
    "term" : {
        "price" : 20
    }
}

通常當查找一個精確值的時候,我們不希望對查詢進行評分計算。只希望對文檔進行包括或排除的計算,所以我們會使用 constant_score 查詢以非評分模式來執行 term 查詢並以一作爲統一評分。(非評分模式查詢也更容易被緩存)

//使用非評分模式查詢
GET /my_store/products/_search
{
    "query" : {
        "constant_score" : { 
            "filter" : {
                "term" : { 
                    "price" : 20
                }
            }
        }
    }
}

過濾器的內部操作

在內部,Elasticsearch 會在運行非評分查詢的時執行多個操作:

  1. 查找匹配文檔.

    term 查詢在倒排索引中查找 XHDK-A-1293-#fJ3 然後獲取包含該 term 的所有文檔。本例中,只有文檔 1 滿足我們要求。

  2. 創建 bitset.

    過濾器會創建一個 bitset (一個包含 0 和 1 的數組),它描述了哪個文檔會包含該 term 。匹配文檔的標誌位是 1 。本例中,bitset 的值爲 [1,0,0,0] 。在內部,它表示成一個 “roaring bitmap”,可以同時對稀疏或密集的集合進行高效編碼。

  3. 迭代 bitset(s)

    一旦爲每個查詢生成了 bitsets ,Elasticsearch 就會循環迭代 bitsets 從而找到滿足所有過濾條件的匹配文檔的集合。執行順序是啓發式的,但一般來說先迭代稀疏的 bitset (因爲它可以排除掉大量的文檔)。

  4. 增量使用計數.

    Elasticsearch 能夠緩存非評分查詢從而獲取更快的訪問,但是它也會不太聰明地緩存一些使用極少的東西。非評分計算因爲倒排索引已經足夠快了,所以我們只想緩存那些我們 知道 在將來會被再次使用的查詢,以避免資源的浪費。

    爲了實現以上設想,Elasticsearch 會爲每個索引跟蹤保留查詢使用的歷史狀態。如果查詢在最近的 256 次查詢中會被用到,那麼它就會被緩存到內存中。當 bitset 被緩存後,緩存會在那些低於 10,000 個文檔(或少於 3% 的總索引數)的段(segment)中被忽略。這些小的段即將會消失,所以爲它們分配緩存是一種浪費。

實際情況並非如此(執行有它的複雜性,這取決於查詢計劃是如何重新規劃的,有些啓發式的算法是基於查詢代價的),理論上非評分查詢 先於 評分查詢執行。非評分查詢任務旨在降低那些將對評分查詢計算帶來更高成本的文檔數量,從而達到快速搜索的目的。

trems(查找多個精確值)

term 查詢對於查找單個值非常有用,但通常我們可能想搜索多個值。 比如我們想要查找價格字段值爲 $20 或 $30 的文檔則可以使用trems
一定要了解 terms包含(contains) 操作,而非 等值(equals) (判斷)。

如果我們有一個 term(詞項)過濾器 { "term" : { "tags" : "search" } } ,它會與以下兩個文檔 **同時 匹配:**儘管第二個文檔包含除 search 以外的其他詞,它還是被匹配並作爲結果返回。

{ "tags" : ["search"] }
{ "tags" : ["search", "open_source"] } 

如果一定期望得到我們前面說的那種行爲(即整個字段完全相等),最好的方式是增加並索引另一個字段, 這個字段用以存儲該字段包含詞項的數量。例如

{ "tags" : ["search"], "tag_count" : 1 }
{ "tags" : ["search", "open_source"], "tag_count" : 2 }

//搜索
GET /my_index/my_type/_search
{
    "query": {
        "constant_score" : {
            "filter" : {
                 "bool" : {
                    "must" : [
                        { "term" : { "tags" : "search" } }, 
                        { "term" : { "tag_count" : 1 } } 
                    ]
                }
            }
        }
    }
}

這個查詢現在只會匹配具有單個標籤 search 的文檔,而不是任意一個包含 search 的文檔。

match(匹配查詢)

匹配查詢 match 是個 核心 查詢。無論需要查詢什麼字段, match 查詢都應該會是首選的查詢方式。它是一個高級 全文查詢 ,這表示它既能處理全文字段,又能處理精確字段。match 查詢主要的應用場景就是進行全文搜索

//初始化查詢數據
PUT /my_index
{ "settings": { "number_of_shards": 1 }} 

POST /my_index/my_type/_bulk
{ "index": { "_id": 1 }}
{ "title": "The quick brown fox" }
{ "index": { "_id": 2 }}
{ "title": "The quick brown fox jumps over the lazy dog" }
{ "index": { "_id": 3 }}
{ "title": "The quick brown fox jumps over the quick dog" }
{ "index": { "_id": 4 }}
{ "title": "Brown fox brown dog" }

//執行查詢
GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": "QUICK!"
        }
    }
}

Elasticsearch 執行上面這個 match 查詢的步驟是:

  1. 檢查字段類型

    標題 title 字段是一個 string 類型( analyzed )已分析的全文字段,這意味着查詢字符串本身也應該被分析(也就是說 title 字段在存入es 時已經會根據選定的分詞器進行分詞,並將分詞結果都建立了索引)。

  2. 分析查詢字符串

    將查詢的字符串 QUICK! 傳入標準分析器中,輸出的結果是單個項 quick 。因爲只有一個單詞項,所以 match 查詢執行的是單個底層 term 查詢。

  3. 查找匹配文檔

    term 查詢在倒排索引中查找 quick 然後獲取一組包含該項的文檔,本例的結果是文檔:1、2 和 3 。

  4. 爲每個文檔評分

    term 查詢計算每個文檔相關度評分 _score ,這是種將詞頻(term frequency,即詞 quick 在相關文檔的 title 字段中出現的頻率)和反向文檔頻率(inverse document frequency,即詞 quick 在所有文檔的 title 字段中出現的頻率),以及字段的長度(即字段越短相關度越高,quick 在文檔的 title 字段中所佔的字符長度比重)相結合的計算方式。

range 範圍查詢

GET /my_store/products/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "range" : {
                    "price" : {
                        "gte" : 20,
                        "lt"  : 40
                    }
                }
            }
        }
    }
}

數字和日期字段的索引方式使高效地範圍計算成爲可能。但字符串卻並非如此,要想對其使用範圍過濾,Elasticsearch 實際上是在爲範圍內的每個詞項都執行 term 過濾器,這會比日期或數字的範圍過濾慢許多。

字符串範圍在過濾 低基數(low cardinality) 字段(即只有少量唯一詞項)時可以正常工作,但是唯一詞項越多,字符串範圍的計算會越慢。

日期支持時間加減

//查找時間戳在過去一個小時內的所有文檔,讓過濾器作爲一個時間 滑動窗口(sliding window) 來過濾文檔
"range" : {
    "timestamp" : {
        "gt" : "now-1h"
    }
}

//查詢 早於 2014 年 1 月 1 日加 1 月(2014 年 2 月 1 日 零時)
"range" : {
    "timestamp" : {
        "gt" : "2014-01-01 00:00:00",
        "lt" : "2014-01-01 00:00:00||+1M" 
    }
}

緩存

精確值查找時, 我們會使用過濾器(filters)。過濾器很重要,因爲它們執行速度非常快,不會計算相關度(直接跳過了整個評分階段)而且很容易被緩存

過濾器的內部操作中,我們介紹了過濾器是如何計算的。其核心實際是採用一個 bitset 記錄與過濾器匹配的文檔。Elasticsearch 積極地把這些 bitset 緩存起來以備隨後使用。一旦緩存成功,bitset 可以複用 任何 已使用過的相同過濾器,而無需再次計算整個過濾器。

這些 bitsets 緩存是“智能”的:它們以增量方式更新。當我們索引新文檔時,只需將那些新文檔加入已有 bitset,而不是對整個緩存一遍又一遍的重複計算。和系統其他部分一樣,過濾器是實時的,我們無需擔心緩存過期問題。

獨立的過濾器緩存

屬於一個查詢組件的 bitsets 是獨立於它所屬搜索請求其他部分的。這就意味着,一旦被緩存,一個查詢可以被用作多個搜索請求。bitsets 並不依賴於它所存在的查詢上下文。這樣使得緩存可以加速查詢中經常使用的部分,從而降低較少、易變的部分所帶來的消耗。

同樣,如果單個請求重用相同的非評分查詢,它緩存的 bitset 可以被單個搜索裏的所有實例所重用。

自動緩存行爲

早期默認的行爲是緩存一切可以緩存的對象。這樣會把一些重用率很低和一些查詢很簡單的的過慮器都緩存了bitsets,這樣就很不合算。緩存這些過濾器的意義不大。開發者難以區分有良好表現的緩存以及無用緩存。

爲了解決問題,Elasticsearch 會基於使用頻次自動緩存查詢。如果一個非評分查詢在最近的 256 次查詢中被使用過(次數取決於查詢類型),那麼這個查詢就會作爲緩存的候選。但是,並不是所有的片段都能保證緩存 bitset 。只有那些文檔數量超過 10,000 (或超過總文檔數量的 3% )纔會緩存 bitset因爲小的片段可以很快的進行搜索和合並,這裏緩存的意義不大

一旦緩存了,非評分計算的 bitset 會一直駐留在緩存中直到它被剔除。剔除規則是基於 LRU 的:一旦緩存滿了,最近最少使用的過濾器會被剔除。

參考文章:https://www.elastic.co/guide/cn/elasticsearch/guide/current/foreword_id.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章