日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Elasticsearch(一)架构及一般性应用

發布時間:2024/10/5 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Elasticsearch(一)架构及一般性应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


?

首先,當我們對記錄進行修改時,es會把數據同時寫到內存緩存區和translog中。而這個時候數據是不能被搜索到的,只有數據形成了segmentFile,才會被搜索到。默認情況下,es每隔一秒鐘執行一次refresh,可以通過參數index.refresh_interval來修改這個刷新間隔或者搜索時加上?refresh=wait_for強制刷新,但是會造成刷新頻次過高會造成性能下降,表示如果1秒內有請求立即更新并可見,執行refresh主要做三件事:

1、所有在內存緩沖區中的文檔被寫入到一個新的segment中,但是沒有調用fsync,因此內存中的數據可能丟失;

2、segment被打開使得里面的文檔能夠被搜索到;

3、清空內存緩沖區;

translog的相當于事務日志,記錄著所有對Elasticsearch的操作記錄,也是對Elasticsearch的一種備份。因為并不是寫到segment就表示數據落到磁盤了,實際上segment是存儲在系統緩存(page cache)中的,只有達到一個周期或者數據量達到一定值,才會flush到磁盤上。這個時候如果系統內存中的segment丟失,是可以通過translog來恢復的。這個flush過程主要做了三件事:

1、往磁盤里寫入commit point信息。

2、文件系統中的segment,fsync到磁盤。

3、清空translog文件。

translog可以保證緩存中的segment的恢復,但translog也不是實時也磁盤的,也就是說,內存中的translog丟了的話,也會有丟失數據的可能。所以translog也要進行flush。translog的flush主要有三個條件:

1、可以設置是否在某些操作之后進行強制flush,比如索引的刪除或批量請求之后。

2、translog大小超過512mb或者超過三十分鐘會強制對segment進行flush,隨后會強制對translog進行flush,這種情況緩存中的translog在flush之后會被清空。

3、默認5s,會強制對translog進行flush。最小值可配置100ms。

6.3版本顯示保留translog文件的最長持續時間。默認為12h。

參考官網:Translog | Elasticsearch Guide [6.3] | Elastic

refresh,flush 和fsync的區別

1.refresh是將緩沖隊列buffer里數據刷入文件緩沖系統生成索引文件segement,該segement數據才能被查詢到,保證查詢屬性可見

2.flush是將索引文件segement數據持久化到硬盤(觸發機制是translog文件超過512mb或者30分鐘強強制刷新segement)

3.fsyncd是將translog 持久化到硬盤(每5秒執行一次) 寫入translog其實也是在內存中。translog 和segement持久化到硬盤是兩回事。

持久化的translog文件中存的是所有索引成segement的數據但還未持久化到硬盤的內容,一旦segement持久化到硬盤translog會清空。

參考:https://elasticsearch.cn/question/3847

索引存儲方式

Elasticsearch是一個建立在全文搜索引擎庫Apache Lucene 基礎上的分布式搜索引擎,Lucene最早的版本是2000年發布的,距今已經18年,是當今最先進,最高效的全功能開源搜索引擎框架。

Lucene

Lucene中包含了四種基本數據類型,分別是:

  • Index:索引,由很多的Document組成。
  • Document:由很多的Field組成,是Index和Search的最小單位。
  • Field:由很多的term組成,包括field_name和field_value。
  • Term:由很多的字節組成,可以分詞,分詞之后每個詞即為一個term。term是索引的最小單元。

上述四種類型在Elasticsearch中同樣存在,意思也一樣。

Lucene中存儲的索引主要分為三種類型:

  • Invert Index,即倒排索引。通過term可以快速查找到包含該term的doc_id。如果Field配置分詞,則分詞后的每個term都會進入倒排索引,如果Field不指定分詞,那該Field的value值則會作為一個term進入倒排。(這里需要注意的是term的長度是有限制的,如果對一個Field不采取分詞,那么不建議該Field存儲過長的值。關于term超長處理)
  • DocValues,即正排索引。采用的是類似數據庫的列式存儲。對于一些特殊需求的字段可以選擇這種索引方式。
  • Store,即原文。存儲整個完整Document的原始信息。

倒排索引是lucene的核心索引類型,采用鏈表的數據結構,倒排索引中的key就是一個term,value就是以doc_id形成的鏈表結構。

Term ? ? ?Doc_1 ? ? ?Doc_2
-------------------------
Quick ? ? ?| ? ? ? ? ? ? ? | ?X
The ? ? ? ? | ? X ? ? ? ? ?|
brown ? ? | ? X ? ? ? ? ?| ?X
dog ? ? ? ? | ? X ? ? ? ? ?|
dogs ? ? ? | ? ? ? ? ? ? ? ?| ?X
fox ? ? ? ? ?| ? X ? ? ? ? ?|
foxes ? ? ?| ? ? ? ? ? ? ? ?| ?X
in ? ? ? ? ? ?| ? ? ? ? ? ? ? ?| ?X
jumped ?| ? X ? ? ? ? ?| ? ?
lazy ? ? ? ?| ? X ? ? ? ? ?| ?X
leap ? ? ? ?| ? ? ? ? ? ? ? ?| ?X
over ? ? ? ?| ? X ? ? ? ? ?| ?X
quick ? ? ?| ? X ? ? ? ? ?|
summer ?| ? ? ? ? ? ? ? | ?X
the ? ? ? ? ?| ? X ? ? ? ? ?|
------------------------

現在,如果我們想搜索 quick brown ,我們只需要查找包含每個詞條的文檔:

Term ? ? ?Doc_1 ? ? ?Doc_2
-------------------------
brown ? | ? X ? ? ? ? ?| ?X
quick ? ?| ? X ? ? ? ? ? |
------------------------
Total ? ? | ? 2 ? ? ? ? ? | ?1

這里分別匹配到了doc1和doc2,但是doc1匹配度要高于doc2。

倒排索引中的value有四種存儲類型:

  • DOCS:只存儲doc_id。
  • DOCS_AND_FREQS:存儲doc_id和詞頻(Term Freq)。
  • DOCS_AND_FREQS_AND_POSITIONS:存儲doc_id、詞頻(Term Freq)和位置。
  • DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS:存儲doc_id、詞頻(Term Freq)、位置和偏移(offset)。

DocValues

正排索引類似關系型數據庫的存儲模式,作用是通過doc_id和field_name可以快速定位到指定doc的特定字段值。DocValues的key是doc_id+field_name,value是field_value。

ES默認會對所有字段進行正排索引,但并不是所有字段都需要DocValues。所以合理配置DocValues可以節省存儲空間。DocValues的使用場景一般是:需要針對某個Field排序、聚合、過濾和script。

Store

存儲的是Document的完整信息,包括所有field_name和field_value。Store的key是doc_id,value是field_name+field_value。對于上訴中需要聚合和排序的Field并沒有開啟DocValues的情況,依然可以實現排序和聚合,會從Store中獲取要排序聚合的字段值。

Elasticsearch在Lucene基礎上的改變

Lucene本身不支持分布式,Elasticsearch通過_routing實現分布式的架構。我們可以通過_routing來實現不同doc分布在不同的Shard上,我們可以自己定義也可以系統自動分配。對于指定_routing的插入和查詢,性能上會更好。

Lucene中沒有主鍵索引,并且id是在Segment中唯一。那么Elasticsearch是如何實現doc_id唯一?Elasticsearch中有個系統字段_id,來決定doc唯一。_id是在用戶可見層度上的id值,實際上Elasticsearch內部會把_id存儲成_uid(_uid =index_type + '#' + _id)。_uid只會存儲倒排和原文,目的就是為了通過id可以快速索引到doc。這里需要注意的是,在Elasticsearch6.x版本以后,一個index只支持一個type,這也就意味著_id和_uid概念幾乎相同。以后的版本中,Elasticsearch會取消type。

Elasticsearch通過_version字段來保證文檔的一致性。更多關于文檔的一致性和鎖機制的參考:ElasticSearch干貨(一):鎖機制

Elasticsearch通過_source字段來存儲doc原文。這個字段非常重要。Lucene的update是覆蓋,是不支持針對doc中特定字段進行修改的。但Elasticsearch支持對特定字段的修改,就是基于_source字段實現的。關于Elasticsearch的update詳細內容,參考:ElasticSearch干貨(二):index、create、update區別

Elasticsearch通過_field_names字段來判斷doc中是否存在某個字段。_field_names的存儲形式為倒排,可以快速判斷出是否包含某個field_name。

Lucene中Segment一旦創建不可修改。那么Elasticsearch如何實現實時修改并索引數據的呢?詳細參考:ElasticSearch原理(三):寫入流程

關于原理和索引先介紹到這里,主要這里還是聚焦與如何實現幾個關鍵需求?

ES延時問題

默認情況下,es每隔一秒鐘執行一次refresh,可以通過參數index.refresh_interval來修改這個刷新間隔或者搜索時加上?refresh=wait_for強制刷新,但是會造成刷新頻次過高會造成性能下降,表示如果1秒內有請求立即更新并可見。另外由于沒有生成segment,也就是說不能通過索引來獲取,但是可以通過直接get by id來獲取單條記錄。

搜索(同時實現精確查詢和模糊查詢和組合查詢)

ES的搜索是分2個階段進行的,即Query階段和Fetch階段。? Query階段比較輕量級,通過查詢倒排索引,獲取滿足查詢結果的文檔ID列表。? 而Fetch階段比較重,需要將每個shard的結果取回,在協調結點進行全局排序。? 通過From+size這種方式分批獲取數據的時候,隨著from加大,需要全局排序并丟棄的結果數量隨之上升,性能越來越差。

1、精確查詢

在elasticsearch 中輸入查詢條件,一般會匹配到很多結果,是因為analyzer的存在:

Each element in the result represents a single term:

{"tokens": [{"token": "text","start_offset": 0,"end_offset": 4,"type": "<ALPHANUM>","position": 1},{"token": "to","start_offset": 5,"end_offset": 7,"type": "<ALPHANUM>","position": 2},{"token": "analyze","start_offset": 8,"end_offset": 15,"type": "<ALPHANUM>","position": 3}] }

必須要將字段設置為not_analyzed?才可以,如下:

PUT /my_store { "mappings" : { "products" : { "properties" : { "productID" : { "type" : "string", "index" : "not_analyzed" } } } } }

2、es組合多個條件進行查詢

1、must、should

GET /test_index/_search
{
  "query": {
    "bool": {
      "must": { "match": { "name": "tom" }},
      "should": [
        { "match": { "hired": true }},
        { "bool": {
          "must": { "match": { "personality": "good" }},
          "must_not": { "match": { "rude": true }}
        }}
      ],
      "minimum_should_match": 1
    }
  }
}

在es中,使用組合條件查詢是其作為搜索引擎檢索數據的一個強大之處,在前幾篇中,簡單演示了es的查詢語法,但基本的增刪改查功能并不能很好的滿足復雜的查詢場景,比如說我們期望像mysql那樣做到拼接復雜的條件進行查詢該如何做呢?es中有一種語法叫bool,通過在bool里面拼接es特定的語法可以做到大部分場景下復雜條件的拼接查詢,也叫復合查詢

首先簡單介紹es中常用的組合查詢用到的關鍵詞,

filter:過濾,不參與打分
must:如果有多個條件,這些條件都必須滿足 and與
should:如果有多個條件,滿足一個或多個即可 or或
must_not:和must相反,必須都不滿足條件才可以匹配到 !非

發生 描述
must
該條款(查詢)必須出現在匹配的文件,并將有助于得分。

filter
子句(查詢)必須出現在匹配的文檔中。然而不像 must查詢的分數將被忽略。Filter子句在過濾器上下文中執行,這意味著評分被忽略,子句被考慮用于高速緩存。

should
子句(查詢)應該出現在匹配的文檔中。如果 bool查詢位于查詢上下文中并且具有mustor filter子句,則bool即使沒有should查詢匹配,文檔也將匹配該查詢 。在這種情況下,這些條款僅用于影響分數。如果bool查詢是過濾器上下文 或者兩者都不存在,must或者filter至少有一個should查詢必須與文檔相匹配才能與bool查詢匹配。這種行為可以通過設置minimum_should_match參數來顯式控制 。

must_not
子句(查詢)不能出現在匹配的文檔中。子句在過濾器上下文中執行,意味著評分被忽略,子句被考慮用于高速緩存。因為計分被忽略,0所有文件的分數被返回。

3、模糊查詢

前綴查詢:匹配包含具有指定前綴的項(not analyzed)的字段的文檔。前綴查詢對應?Lucene?的?PrefixQuery?。

案例 GET /_search { "query": {"prefix" : { "user" : { "value" : "ki", "boost" : 2.0 } }} }

正則表達式查詢:egexp?(正則表達式)查詢允許您使用正則表達式進行項查詢。有關支持的正則表達式語言的詳細信息,請參閱正則表達式語法。第一個句子中的 “項查詢” 意味著?Elasticsearch?會將正則表達式應用于由該字段生成的項,而不是字段的原始文本。注意:?regexp?(正則表達式)查詢的性能很大程度上取決于所選的正則表達式。匹配一切像?“.*”?,是非常慢的,使用回顧正則表達式也是如此。如果可能,您應該嘗試在正則表達式開始之前使用長前綴。通配符匹配器?“.*?+”?將主要降低性能。

案例 GET /_search {"query": {"regexp":{"name.first":{"value":"s.*y","boost":1.2}}} } ‘

通配符查詢:匹配與通配符表達式具有匹配字段的文檔(not analyzed)。支持的通配符是 “*”,它匹配任何字符序列(包括空字符);還有 “?”,它匹配任何單個字符。請注意,此查詢可能很慢,因為它需要迭代多個項。為了防止極慢的通配符查詢,通配符項不應以通配符 “*” 或 “?” 開頭。通配符查詢對應?Lucene?的?WildcardQuery?。

案例 GET /_search {"query": {"wildcard" : { "user" : { "value" : "ki*y", "boost" : 2.0 } }} }

###模糊查詢數據量越大效率越低,當查詢內容較多,數據量較大時建議將該字段設置成text進行分詞,然后通過match進行匹配。

排序與相關性

默認情況下,返回的結果是按照 相關性 進行排序的——最相關的文檔排在最前。 在本章的后面部分,我們會解釋 相關性 意味著什么以及它是如何計算的, 不過讓我們首先看看 sort 參數以及如何使用它。

1、排序

為了按照相關性來排序,需要將相關性表示為一個數值。在 Elasticsearch 中, 相關性得分 由一個浮點數進行表示,并在搜索結果中通過 _score 參數返回, 默認排序是 _score 降序。

有時,相關性評分對你來說并沒有意義。例如,下面的查詢返回所有 user_id 字段包含 1 的結果:

GET /_search {"query" : {"bool" : {"filter" : {"term" : {"user_id" : 1}}}} }

里沒有一個有意義的分數:因為我們使用的是 filter (過濾),這表明我們只希望獲取匹配 user_id: 1 的文檔,并沒有試圖確定這些文檔的相關性。 實際上文檔將按照隨機順序返回,并且每個文檔都會評為零分。

1.1、按照字段的值排序

在這個案例中,通過時間來對 tweets 進行排序是有意義的,最新的 tweets 排在最前。 我們可以使用 sort 參數進行實現:

GET /_search {"query" : {"bool" : {"filter" : { "term" : { "user_id" : 1 }}}},"sort": { "date": { "order": "desc" }} }

1.2、多級排序

假定我們想要結合使用 date 和 _score 進行查詢,并且匹配的結果首先按照日期排序,然后按照相關性排序:

GET /_search {"query" : {"bool" : {"must": { "match": { "tweet": "manage text search" }},"filter" : { "term" : { "user_id" : 2 }}}},"sort": [{ "date": { "order": "desc" }},{ "_score": { "order": "desc" }}] }

排序條件的順序是很重要的。結果首先按第一個條件排序,僅當結果集的第一個 sort 值完全相同時才會按照第二個條件進行排序,以此類推。

多級排序并不一定包含 _score 。你可以根據一些不同的字段進行排序, 如地理距離或是腳本計算的特定值。

1.3、字段多值的排序

一種情形是字段有多個值的排序, 需要記住這些值并沒有固有的順序;一個多值的字段僅僅是多個值的包裝,這時應該選擇哪個進行排序呢?

對于數字或日期,你可以將多值字段減為單值,這可以通過使用 min 、 max 、 avg 或是 sum 排序模式 。 例如你可以按照每個 date 字段中的最早日期進行排序,通過以下方法:

"sort": {"dates": {"order": "asc","mode": "min"} }
  • 更多詳情清參考此文;

如何在elasticsearch里面使用分頁功能

from + size 淺分頁

"淺"分頁可以理解為簡單意義上的分頁。它的原理很簡單,就是查詢前20條數據,然后截斷前10條,只返回10-20的數據。這樣其實白白浪費了前10條的查詢。

GET test_dev/_search {"query": {"bool": {"filter": [{"term": {"age": 28}}]}},"size": 10,"from": 20,"sort": [{"timestamp": {"order": "desc"},"_id": {"order": "desc"}}] }

其中,from定義了目標數據的偏移值,size定義當前返回的數目。默認from為0,size為10,即所有的查詢默認僅僅返回前10條數據。

在這里有必要了解一下from/size的原理:
因為es是基于分片的,假設有5個分片,from=100,size=10。則會根據排序規則從5個分片中各取回100條數據數據,然后匯總成500條數據后選擇最后面的10條數據。

做過測試,越往后的分頁,執行的效率越低??傮w上會隨著from的增加,消耗時間也會增加。而且數據量越大,就越明顯!

es默認的from+size的分頁方式返回的結果數據集不能超過1萬點,超過之后返回的數據越多性能就越低;

這是因為es要計算相似度排名,需要排序整個整個結果集,假設我們有一個index它有5個shard,現在要讀取1000到1010之間的這10條數據,es內部會在每個shard上讀取1010條數據,然后返回給計算節點,這里有朋友可能問為啥不是10條數據而是1010條呢?這是因為某個shard上的10條數據,可能還沒有另一個shard上top10之后的數據相似度高,所以必須全部返回,然后在計算節點上,重新對5050條數據進行全局排序,最后在選取top 10出來,這里面排序是非常耗時的,所以這個數量其實是指數級增長的,到后面分頁數量越多性能就越下降的厲害,而且大量的數據排序會占用jvm的內存,很有可能就OOM了,這也是為什么es默認不允許讀取超過1萬條數據的原因。?

scroll 深分頁

from+size查詢在10000-50000條數據(1000到5000頁)以內的時候還是可以的,但是如果數據過多的話,就會出現深分頁問題。

為了解決上面的問題,elasticsearch提出了一個scroll滾動的方式。
scroll 類似于sql中的cursor,使用scroll,每次只能獲取一頁的內容,然后會返回一個scroll_id。根據返回的這個scroll_id可以不斷地獲取下一頁的內容,所以scroll并不適用于有跳頁的情景。

GET test_dev/_search?scroll=5m {"query": {"bool": {"filter": [{"term": {"age": 28}}]}},"size": 10,"from": 0,"sort": [{"timestamp": {"order": "desc"},"_id": {"order": "desc"}}] }
  • scroll=5m表示設置scroll_id保留5分鐘可用。
  • 使用scroll必須要將from設置為0。
  • size決定后面每次調用_search搜索返回的數量
  • 然后我們可以通過數據返回的_scroll_id讀取下一頁內容,每次請求將會讀取下10條數據,直到數據讀取完畢或者scroll_id保留時間截止:

    GET _search/scroll {"scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAJZ9Fnk1d......","scroll": "5m" }

    注意:請求的接口不再使用索引名了,而是 _search/scroll,其中GET和POST方法都可以使用。

    scroll刪除
    根據官方文檔的說法,scroll的搜索上下文會在scroll的保留時間截止后自動清除,但是我們知道scroll是非常消耗資源的,所以一個建議就是當不需要了scroll數據的時候,盡可能快的把scroll_id顯式刪除掉。

    清除指定的scroll_id:

    DELETE _search/scroll/DnF1ZXJ5VGhlbkZldGNo.....

    清除所有的scroll:

    DELETE _search/scroll/_all

    search_after 深分頁

    scroll 的方式,官方的建議不用于實時的請求(一般用于數據導出),因為每一個 scroll_id 不僅會占用大量的資源,而且會生成歷史快照,對于數據的變更不會反映到快照上。

    search_after 分頁的方式是根據上一頁的最后一條數據來確定下一頁的位置,同時在分頁請求的過程中,如果有索引數據的增刪改查,這些變更也會實時的反映到游標上。但是需要注意,因為每一頁的數據依賴于上一頁最后一條數據,所以無法跳頁請求。

    為了找到每一頁最后一條數據,每個文檔必須有一個全局唯一值,官方推薦使用 _uid 作為全局唯一值,其實使用業務層的 id 也可以。

    GET test_dev/_search {"query": {"bool": {"filter": [{"term": {"age": 28}}]}},"size": 20,"from": 0,"sort": [{"timestamp": {"order": "desc"},"_id": {"order": "desc"}}] }
  • 使用search_after必須要設置from=0。
  • 這里我使用timestamp和_id作為唯一值排序。
  • 我們在返回的最后一條數據里拿到sort屬性的值傳入到search_after。
  • 使用sort返回的值搜索下一頁:

    GET test_dev/_search {"query": {"bool": {"filter": [{"term": {"age": 28}}]}},"size": 10,"from": 0,"search_after": [1541495312521,"d0xH6GYBBtbwbQSP0j1A"],"sort": [{"timestamp": {"order": "desc"},"_id": {"order": "desc"}}] } </div>

    只是整合記錄少有原創

    總結

    以上是生活随笔為你收集整理的Elasticsearch(一)架构及一般性应用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。