Elasticsearch 是默認延遲加載fielddata到內存裏的。當elasticsearch第一次遇到一個查詢須要一個指定field的fielddata的時候,就會把索引的每一個段中整個field加載到內存。對於小段,這是個能夠忽略不計的時間,可是若是你有一些5G大小的段而且須要加載10GB的fielddata到內存裏,這個過程須要數十秒,習慣於秒內響應時間的用戶會被網突如其來的遲鈍所打擊。java
有三種方法應對這種延遲尖峯:
Eagerly load fielddata(餓漢式(預)加載fielddata)
Eagerly load global ordinals(餓漢式(預)加載全局序數)
Prepopulate caches with warmers (使用warmer提早加載緩存)。json
全部這些都是一個意思:預先加載fielddata到內存裏,這樣當用戶須要執行一個搜索的時候就感覺不到延遲了。api
餓漢式(預)加載fielddata緩存
首先是預先加載(而不是默認的延遲加載),當一個新的段造成時(無亂是刷新,寫入或者是合併),能夠預先加載的field會提早把段的fielddata加載到內存裏,在這段能夠用於搜索以前。數據結構
這意味着當你第一次查詢的時候,若是碰到在這個段上,你不須要再觸發加載fielddata的操做,它們已經在內存中了,這會防止你的用戶遇到一些冷到緩存而發生延遲尖峯。app
預先加載是基於每一個field的,因此你能夠控制哪些field進行預加載。electron
PUT /music/_mapping/_song { "price_usd": { "type": "integer", "fielddata": { "loading" : "eager" } } }
備註: 經過設置fielddata.loading: eager,告訴elasticsearch預先加載這個field的內容到內存裏。elasticsearch
fielddata的加載能夠設置成餓漢模式(預先加載)仍是懶惰模式(延遲加載),使用update-mapping的api。ide
預先加載是簡單對fielddata加載的開銷的轉移,從查詢時間轉義到刷新時刻。性能
大段的刷新時間會比小段的時間長,一般大段的產生都是由哪些已經可搜素的小段合併而來的,因此慢一點的刷新時間不是那麼重要(譯者注:意思是大段的刷新時間長不影響你的搜索,在大段合併成前的小段能夠用於搜索)。
全局序數
其中一項用於減小string類型的fielddata佔用內存的技術叫作序數。
假設咱們有十億條文檔,每一個文檔都有一個status的field,只有三個值:status_pending, status_published, status_deleted,若是咱們把全部的status加載到內存裏,每一個文檔須要14-16byte,也就是說15GB
相反,咱們能夠確認這三個特殊的字符串,對他們排序,依次編號0,1,2
Ordinal | Term ------------------- 0 | status_deleted 1 | status_pending 2 | status_published
序號對應的字符串值要在序號列表中存儲一次,每一個文檔只要使用他們的編號來表示他們所包含的值就能夠了。
Doc | Ordinal ------------------------- 0 | 1 # pending 1 | 1 # pending 2 | 2 # published 3 | 0 # deleted
這個能夠把15GB的內存佔用減小到小於1GB
可是這裏有個問題,記住fielddata緩存是針對每一個段的,若是一個段包含兩個狀態-status_deleted 和 status_published,這個序號是(0 和1),若是其它段中有三個不一樣的狀態,那相同的序號的含義在兩個段中是不一樣的。
若是咱們試圖執行一個status 的field的彙總操做(aggregation ),咱們須要在實際的字符串上進行彙總。覺得着咱們要在全部的段上識別出有相同值的文檔,一個單純的方式就是在每一個段上進行彙總操做,從每一個段上返回字符串的值,而後合併他們獲得最終的結果,這樣是可行的,這會很慢很耗CPU。
想反,咱們實驗了一個數據結構叫作全局序數,全局序號只是在fielddata之上,一小部份內存數據結構,把全部的段的惟一值存儲在一個序號列表裏,就像前面咱們描述的那樣。
如今,咱們的彙總操做只要在全局序號上進行彙總,把序號轉換成實際字符串,只要在最後一個彙總中進行就能夠了,這大大提升了在只要三四個值的field上進行彙總(還有排序)操做的性能。
構建全局序數
固然,天下沒有免費的午飯,全局序數是針對一個索引的全部段的,因此若是增長了一個段或者刪除了一個段,這個全局序數都要進行重建,長袖須要讀取每一個段的每一個惟一term。存儲基數越大,惟一tems越多,這個過程會更長。
全局序數是建立在內存裏的fielddata和doc values之上的,實際上,他們也是doc values性能表現的主要緣由。
和fielddata的加載同樣,全局序數的構建默認也是延遲進行的,當第一個請求須要fielddata時候,會觸發全局序數的構建,和你的field的基數有關,這可能致使用戶的一個延遲響應。一旦全局序數構建完成,他們將被使用,直到索引中的段發生變化:刷新,寫入和合並。
餓漢式(預先加載)全局序數
PUT /music/_mapping/_song { "song_title": { "type": "string", "fielddata": { "loading" : "eager_global_ordinals" } } }
注:設置爲eager_global_ordinals 也表明實現了預先加載fielddata。
就像預先加載fielddata同樣,全局序數會在一個新的段可進行搜索以前進行構建。
注意:序數只用於字符串類型的field,數值類型(整型,地理位置,時間等)都不須要一個序數映射,由於他們自身就是一個內存的序數映射。
因此,你只能開啓字符串類型的全局序數的預加載。
文檔的值也能夠有他們的全局序數:
PUT /music/_mapping/_song { "song_title": { "type": "string", "doc_values": true, "fielddata": { "loading" : "eager_global_ordinals" } } }
注:這種狀況,fielddata就不加載到內存裏了,而是文檔的values會被加載到系統級緩存裏。
和fielddata的預加載不一樣,對全局序數的預加載會對數據的實時性有影響,隊友一個很大基數的field,構建全局序數會致使數秒的延遲刷新,是把時間花在每次刷新上呢,仍是每次刷新以後的第一次查詢上面這是一個選擇。若是你插入數據頻繁,查詢不多,最好是把代價花在查詢時刻,而不是每次刷新上。
注意:讓你的全局序號本身處理,若是你有一個很大基數的field,致使很長時候構建,你能夠增長refresh_interval 配置,這樣回增長全局序數的有效時間,減小CPU的利用率,一樣構建全局序數的次數也會減小。
Index warmers(索引熱身)
最後,咱們到了index warmers,wamers能夠提前加載fielddata和全局序數,可是他還有一個目的。一個索引的warmer容許你指定一個查詢或者匯聚操做在你的新段可被查詢以前執行,這個idea就叫作預熱、或者叫暖身,緩存等,那你的用戶就永遠不會遇到延遲尖峯時刻了。
期初,這個warmers的最重要的用處是爲了確保fielddata的預先加載,這一般是開銷最大的部分,這是咱們以前討論的利用這個技術控制的最好的地方,然而,warmers能夠用於構建filter 緩存,也能夠用於預加載fielddata,這個目的隨你選擇。
先讓咱們註冊一個warner再去討論發生了什麼:
PUT /music/_warmer/warmer_1 { "query" : { "filtered" : { "filter" : { "bool": { "should": [ { "term": { "tag": "rock" }}, { "term": { "tag": "hiphop" }}, { "term": { "tag": "electronics" }} ] } } } }, "aggs" : { "price" : { "histogram" : { "field" : "price", "interval" : 10 } } } }
warmers是註冊在一個特定的索引上面的,每個warmer都有一個惟一的id,由於你能夠爲一個索引指定多個warmer。
而後你能夠之低昂一個查詢,或者多個查詢,他包含查詢語句,filters,aggregations,sort,腳本等有效的查詢語句。重點是要註冊那些你的用戶將要進行,有表明性的查詢,從而適當的緩存能夠被預先加載。
當一個新的段產生的時候,elasticsearch就會執行你這些warmers裏的查詢語句。執行這些查詢會致使緩存進行加載,只有索引warmers都被執行了,新的段纔會變成可搜素狀態。
注意:相似於預先加載,warmers 把對冷緩存的開銷轉移到了刷新時刻。當你註冊了warmers,你必須是通過明智判斷的。你能夠增長成千上萬個wamer確保預熱全部的緩存,可是這將極大的增長一個新的段從建立變爲可搜索的時間。
實踐中,選擇幾條表明大部分用戶的查詢語句進行註冊。
一些管理方面的細節(例如獲取存在的warmer,刪除warmer)就不在這裏過多解釋了,閱讀warmer相關文檔,獲取其它細節。