Elasticsearch 學習總結 - 相關配置補充說明

散盡浮華發表於2019-04-10

 

一.   Elasticsearch的基本概念

term
索引詞,在elasticsearch中索引詞(term)是一個能夠被索引的精確值。foo,Foo Foo幾個單詞是不相同的索引詞。索引詞(term)是可以通過term查詢進行準確的搜尋。

text
文字是一段普通的非結構化文字,通常,文字會被分析稱一個個的索引詞,儲存在elasticsearch的索引庫中,為了讓文字能夠進行搜尋,文字欄位需要事先進行分析;當對文字中的關鍵詞進行查詢的時候,搜尋引擎應該根據搜尋條件搜尋出原文字。

analysis
分析是將文字轉換為索引詞的過程,分析的結果依賴於分詞器,比如: FOO BAR, Foo-Bar, foo bar這幾個單詞有可能會被分析成相同的索引詞foo和bar,這些索引詞儲存在elasticsearch的索引庫中。當用 FoO:bAR進行全文搜尋的時候,搜尋引擎根據匹配計算也能在索引庫中搜尋出之前的內容。這就是elasticsearch的搜尋分析。

cluster
代表一個叢集,叢集中有多個節點,其中有一個為主節點,這個主節點是可以通過選舉產生的,主從節點是對於叢集內部來說的。es的一個概念就是去中心化,字面上理解就是無中心節點,這是對於叢集外部來說的,因為從外部來看es叢集,在邏輯上是個整體,你與任何一個節點的通訊和與整個es叢集通訊是等價的。

node
每一個執行例項稱為一個節點,每一個執行例項既可以在同一機器上,也可以在不同的機器上。所謂執行例項,就是一個伺服器程式,在測試環境中可以在一臺伺服器上執行多個伺服器程式,在生產環境中建議每臺伺服器執行一個伺服器程式。

routing
路由,當儲存一個文件的時候,他會儲存在一個唯一的主分片中,具體哪個分片是通過雜湊值的進行選擇。預設情況下,這個值是由文件的id生成。如果文件有一個指定的父文件,從父文件ID中生成,該值可以在儲存文件的時候進行修改。

index
Elasticsearch裡的索引概念是名詞而不是動詞,在elasticsearch裡它支援多個索引。 一個索引就是一個擁有相似特徵的文件的集合。比如說,你可以有一個客戶資料的索引,另一個產品目錄的索引,還有一個訂單資料的索引。一個索引由一個名字來 標識(必須全部是小寫字母的),並且當我們要對這個索引中的文件進行索引、搜尋、更新和刪除的時候,都要使用到這個名字。在一個叢集中,你能夠建立任意多個索引。

type
代表型別,在一個索引中,你可以定義一種或多種型別。一個型別是你的索引的一個邏輯上的分類/分割槽,其語義完全由你來定。通常,會為具有一組相同欄位的文件定義一個型別。比如說,我們假設你運營一個部落格平臺 並且將你所有的資料儲存到一個索引中。在這個索引中,你可以為使用者資料定義一個型別,為部落格資料定義另一個型別,當然,也可以為評論資料定義另一個型別。

document
一個文件是一個可被索引的基礎資訊單元。比如,你可以擁有某一個客戶的文件、某一個產品的一個文件、某個訂單的一個文件。文件以JSON格式來表示,而JSON是一個到處存在的網際網路資料互動格式。在一個index/type裡面,你可以儲存任意多的文件。注意,一個文件物理上存在於一個索引之中,但文件必須被索引/賦予一個索引的type。

shards
代表索引分片,es可以把一個完整的索引分成多個分片,這樣的好處是可以把一個大的索引拆分成多個,分佈到不同的節點上。構成分散式搜尋。分片的數量只能在索引建立前指定,並且索引建立後不能更改。

primary shard
主分片,每個文件都儲存在一個分片中,當你儲存一個文件的時候,系統會首先儲存在主分片中,然後會複製到不同的副本中。預設情況下,一個索引有5個主分片。你可以在事先制定分片的數量,當分片一旦建立,分片的數量則不能修改。

replica shard
副本分片,每一個分片有零個或多個副本。副本主要是主分片的複製,其中有兩個目的:
-> 增加高可用性:當主分片失敗的時候,可以從副本分片中選擇一個作為主分片。
-> 提高效能:當查詢的時候可以到主分片或者副本分片中進行查詢。預設情況下,一個主分配有一個副本,但副本的數量可以在後面動態的配置增加。副本必須部署在不同的節點上,不能部署在和主分片相同的節點上。

template
索引可使用預定義的模板進行建立,這個模板稱作Index templates。模板設定包括settings和mappings。

mapping
對映像關聯式資料庫中的表結構,每一個索引都有一個對映,它定義了索引中的每一個欄位型別,以及一個索引範圍內的設定。一個對映可以事先被定義,或者在第一次儲存文件的時候自動識別。

field
一個文件中包含零個或者多個欄位,欄位可以是一個簡單的值(例如字串、整數、日期),也可以是一個陣列或物件的巢狀結構。欄位類似於關聯式資料庫中的表中的列。每個欄位都對應一個欄位型別,例如整數、字串、物件等。欄位還可以指定如何分析該欄位的值。

source field
預設情況下,你的原文件將被儲存在_source這個欄位中,當你查詢的時候也是返回這個欄位。這允許您可以從搜尋結果中訪問原始的物件,這個物件返回一個精確的json字串,這個物件不顯示索引分析後的其他任何資料。

id
id是一個檔案的唯一標識,如果在存庫的時候沒有提供id,系統會自動生成一個id,文件的index/type/id必須是唯一的。

recovery
代表資料恢復或叫資料重新分佈,es在有節點加入或退出時會根據機器的負載對索引分片進行重新分配,掛掉的節點重新啟動時也會進行資料恢復。

River
代表es的一個資料來源,也是其它儲存方式(如:資料庫)同步資料到es的一個方法。它是以外掛方式存在的一個es服務,通過讀取river中的資料並把它索引到es中,官方的river有couchDB的,RabbitMQ的,Twitter的,Wikipedia的,river這個功能將會在後面的檔案中重點說到。

gateway
代表es索引的持久化儲存方式,es預設是先把索引存放到記憶體中,當記憶體滿了時再持久化到硬碟。當這個es叢集關閉再重新啟動時就會從gateway中讀取索引資料。es支援多種型別的gateway,有本地檔案系統(預設),分散式檔案系統,Hadoop的HDFS和amazon的s3雲端儲存服務。

discovery.zen
代表es的自動發現節點機制,es是一個基於p2p的系統,它先通過廣播尋找存在的節點,再通過多播協議來進行節點之間的通訊,同時也支援點對點的互動。

Transport
代表es內部節點或叢集與客戶端的互動方式,預設內部是使用tcp協議進行互動,同時它支援http協議(json格式)、thrift、servlet、memcached、zeroMQ等的傳輸協議(通過外掛方式整合)。

二、Elasticsearch 的配置檔案詳細說明

elasticsearch的配置檔案是在elasticsearch目錄下的config檔案下的elasticsearch.yml,同時它的日誌檔案在elasticsearch目錄下的logs,由於elasticsearch的日誌也是使用log4j來寫日誌的,所以其配置模式與log4j基本相同。

Cluster部分
cluster.name: kevin-elk (預設值:elasticsearch)
cluster.name可以確定你的叢集名稱,當你的elasticsearch叢集在同一個網段中elasticsearch會自動的找到具有相同cluster.name 的elasticsearch服務。所以當同一個網段具有多個elasticsearch叢集時cluster.name就成為同一個叢集的標識。

Node部分
node.name: "elk-node01"  節點名,可自動生成也可手動配置。
node.master: true (預設值:true)  允許一個節點是否可以成為一個master節點,es是預設叢集中的第一臺機器為master,如果這臺機器停止就會重新選舉master。
node.client  當該值設定為true時,node.master值自動設定為false,不參加master選舉。
node.data: true (預設值:true)  允許該節點儲存資料。
node.rack  無預設值,為節點新增自定義屬性。
node.max_local_storage_nodes: 1 (預設值:1) 設定能執行的節點數目,一般採用預設的1即可,因為我們一般也只在一臺機子上部署一個節點。

配置檔案中給出了三種配置高效能叢集拓撲結構的模式,如下:
->  workhorse:如果想讓節點從不選舉為主節點,只用來儲存資料,可作為負載器
node.master: false
node.data: true
-> coordinator:如果想讓節點成為主節點,且不儲存任何資料,並保有空閒資源,可作為協調器
node.master: true
node.data: false
-> search load balancer:(fetching data from nodes, aggregating results, etc.理解為搜尋的負載均衡節點,從其他的節點收集資料或聚集後的結果等),客戶端節點可以直接將請求發到資料存在的節點,而不用查詢所有的資料節點,另外可以在它的上面可以進行資料的彙總工作,可以減輕資料節點的壓力。
node.master: false
node.data: false

另外配置檔案提到了幾種監控es叢集的API或方法:
Cluster Health API:http://127.0.0.1:9200/_cluster/health
Node Info API:http://127.0.0.1:9200/_nodes

還有圖形化工具:
https://www.elastic.co/products/marvel
https://github.com/karmi/elasticsearch-paramedic
https://github.com/hlstudio/bigdesk
https://github.com/mobz/elasticsearch-head

Indices部分

index.number_of_shards: 5 (預設值為5)    設定預設索引分片個數。
index.number_of_replicas: 1(預設值為1)    設定索引的副本個數

伺服器夠多,可以將分片提高,儘量將資料平均分佈到叢集中,增加副本數量可以有效的提高搜尋效能。
需要注意 "number_of_shards" 是索引建立後一次生成的,後續不可更改設定 "number_of_replicas" 是可以通過update-index-settings API實時修改設定。

Indices Circuit Breaker
elasticsearch包含多個circuit breaker來避免操作的記憶體溢位。每個breaker都指定可以使用記憶體的限制。另外有一個父級breaker指定所有的breaker可以使用的總記憶體

indices.breaker.total.limit  所有breaker使用的記憶體值,預設值為 JVM 堆記憶體的70%,當記憶體達到最高值時會觸發記憶體回收。

Field data circuit breaker    允許elasticsearch預算待載入field的記憶體,防止field資料載入引發異常

indices.breaker.fielddata.limit     field資料使用記憶體限制,預設為JVM 堆的60%。
indices.breaker.fielddata.overhead  elasticsearch使用這個常數乘以所有fielddata的實際值作field的估算值。預設為 1.03。

請求斷路器(Request circuit breaker) 允許elasticsearch防止每個請求的資料結構超過了一定量的記憶體

indices.breaker.request.limit     request數量使用記憶體限制,預設為JVM堆的40%。
indices.breaker.request.overhead   elasticsearch使用這個常數乘以所有request佔用記憶體的實際值作為最後的估算值。預設為 1。

Indices Fielddata cache
欄位資料快取主要用於排序欄位和計算聚合。將所有的欄位值載入到記憶體中,以便提供基於文件快速訪問這些值

indices.fielddata.cache.size:unbounded 
設定欄位資料快取的最大值,值可以設定為節點堆空間的百分比,例:30%,可以值絕對值,例:12g。預設為無限。
該設定是靜態設定,必須配置到叢集的每個節點。

Indices Node query cache
query cache負責快取查詢結果,每個節點都有一個查詢快取共享給所有的分片。快取實現一個LRU驅逐策略:當快取使用已滿,最近最少使用的資料將被刪除,來快取新的資料。query cache只快取過濾過的上下文

indices.queries.cache.size
查詢請求快取大小,預設為10%。也可以寫為絕對值,例:512m。
該設定是靜態設定,必須配置到叢集的每個資料節點。

Indexing Buffer
索引緩衝區用於儲存新索引的文件。緩衝區寫滿,緩衝區的檔案才會寫到硬碟。緩衝區劃分給節點上的所有分片。
Indexing Buffer的配置是靜態配置,必須配置都叢集中的所有資料節點

indices.memory.index_buffer_size
允許配置百分比和位元組大小的值。預設10%,節點總記憶體堆的10%用作索引緩衝區大小。

indices.memory.min_index_buffer_size
如果index_buffer_size被設定為一個百分比,這個設定可以指定一個最小值。預設為 48mb。

indices.memory.max_index_buffer_size
如果index_buffer_size被設定為一個百分比,這個設定可以指定一個最小值。預設為無限。

indices.memory.min_shard_index_buffer_size
設定每個分片的最小索引緩衝區大小。預設為4mb。

Indices Shard request cache
當一個搜尋請求是對一個索引或者多個索引的時候,每一個分片都是進行它自己內容的搜尋然後把結果返回到協調節點,然後把這些結果合併到一起統一對外提供。分片快取模組快取了這個分片的搜尋結果。這使得搜尋頻率高的請求會立即返回。

注意:請求快取只快取查詢條件 size=0的搜尋,快取的內容有hits.total, aggregations, suggestions,不快取原始的hits。通過now查詢的結果將不快取。
快取失效:只有在分片的資料實際上發生了變化的時候重新整理分片快取才會失效。重新整理的時間間隔越長,快取的資料越多,當快取不夠的時候,最少使用的資料將被刪除。

快取過期可以手工設定,例如:

curl -XPOST 'localhost:9200/kimchy,elasticsearch/_cache/clear?request_cache=true'

預設情況下快取未啟用,但在建立新的索引時可啟用,例如:

curl -XPUT localhost:9200/my_index -d'
{
  "settings": {
    "index.requests.cache.enable": true
  }
} 
'

當然也可以通過動態引數配置來進行設定:

curl -XPUT localhost:9200/my_index/_settings -d'
{ "index.requests.cache.enable": true }
'

每請求啟用快取,查詢字串引數request_cache可用於啟用或禁用每個請求的快取。例如:

curl 'localhost:9200/my_index/_search?request_cache=true' -d'
{
  "size": 0,
  "aggs": {
    "popular_colors": {
      "terms": {
        "field": "colors"
      }
    }
  }
}
'

注意:如果你的查詢使用了一個指令碼,其結果是不確定的(例如,它使用一個隨機函式或引用當前時間)應該設定 request_cache=false 禁用請求快取。

快取key,資料的快取是整個JSON,這意味著如果JSON發生了變化 ,例如如果輸出的順序順序不同,快取的內容江將會不同。不過大多數JSON庫對JSON鍵的順序是固定的。

分片請求快取是在節點級別進行管理的,並有一個預設的值是JVM堆記憶體大小的1%,可以通過配置檔案進行修改。 例如: indices.requests.cache.size: 2%

分片快取大小的檢視方式:

curl 'localhost:9200/_stats/request_cache?pretty&human'

或者

curl 'localhost:9200/_nodes/stats/indices/request_cache?pretty&human'

Indices Recovery

indices.recovery.concurrent_streams  限制從其它分片恢復資料時最大同時開啟併發流的個數。預設為 3。
indices.recovery.concurrent_small_file_streams  從其他的分片恢復時開啟每個節點的小檔案(小於5M)流的數目。預設為 2。
indices.recovery.file_chunk_size  預設為 512kb。
indices.recovery.translog_ops  預設為 1000。
indices.recovery.translog_size  預設為 512kb。
indices.recovery.compress  恢復分片時,是否啟用壓縮。預設為 true。
indices.recovery.max_bytes_per_sec  限制從其它分片恢復資料時每秒的最大傳輸速度。預設為 40mb。

Indices TTL interval

indices.ttl.interval 允許設定多久過期的檔案會被自動刪除。預設值是60s。
indices.ttl.bulk_size 設定批量刪除請求的數量。預設值為1000。

Paths部分

path.conf: /path/to/conf  配置檔案儲存位置。
path.data: /path/to/data  資料儲存位置,索引資料可以有多個路徑,使用逗號隔開。
path.work: /path/to/work  臨時檔案的路徑 。
path.logs: /path/to/logs  日誌檔案的路徑 。
path.plugins: /path/to/plugins  外掛安裝路徑 。

Memory部分
bootstrap.mlockall: true(預設為false)
鎖住記憶體,當JVM進行記憶體轉換的時候,es的效能會降低,所以可以使用這個屬性鎖住記憶體。同時也要允許elasticsearch的程式可以鎖住記憶體,linux下可以通過`ulimit -l unlimited`命令,或者在/etc/sysconfig/elasticsearch檔案中取消 MAX_LOCKED_MEMORY=unlimited 的註釋即可。如果使用該配置則ES_HEAP_SIZE必須設定,設定為當前可用記憶體的50%,最大不能超過31G,預設配置最小為256M,最大為1G。

可以通過請求檢視mlockall的值是否設定:

curl http://localhost:9200/_nodes/process?pretty

如果mlockall的值是false,則設定失敗。可能是由於elasticsearch的臨時目錄(/tmp)掛載的時候沒有可執行許可權。
可以使用下面的命令來更改臨時目錄:

./bin/elasticsearch -Djna.tmpdir=/path/to/new/dir

Network 、Transport and HTTP 部分

network.bind_host
設定繫結的ip地址,可以是ipv4或ipv6的。

network.publish_host
設定其它節點和該節點互動的ip地址,如果不設定它會自動設定,值必須是個真實的ip地址。

network.host
同時設定bind_host和publish_host兩個引數,值可以為網路卡介面、127.0.0.1、私有地址以及公有地址。

http_port
接收http請求的繫結埠。可以為一個值或埠範圍,如果是一個埠範圍,節點將繫結到第一個可用埠。預設為:9200-9300。

transport.tcp.port
節點通訊的繫結埠。可以為一個值或埠範圍,如果是一個埠範圍,節點將繫結到第一個可用埠。預設為:9300-9400。

transport.tcp.connect_timeout
套接字連線超時設定,預設為 30s。

transport.tcp.compress
設定為true啟用節點之間傳輸的壓縮(LZF),預設為false。

transport.ping_schedule
定時傳送ping訊息保持連線,預設transport客戶端為5s,其他為-1(禁用)。

httpd.enabled
是否使用http協議提供服務。預設為:true(開啟)。

http.max_content_length
最大http請求內容。預設為100MB。如果設定超過100MB,將會被MAX_VALUE重置為100MB。

http.max_initial_line_length
http的url的最大長度。預設為:4kb。

http.max_header_size
http中header的最大值。預設為8kb。

http.compression
支援壓縮(Accept-Encoding)。預設為:false。

http.compression_level
定義壓縮等級。預設為:6。

http.cors.enabled
啟用或禁用跨域資源共享。預設為:false。

http.cors.allow-origin
啟用跨域資源共享後,預設沒有源站被允許。在//中填寫域名支援正則,例如 /https?:\/\/localhost(:[0-9]+)?/。 * 是有效的值,但是開放任何域名的跨域請求被認為是有安全風險的elasticsearch例項。

http.cors.max-age
瀏覽器傳送‘preflight’OPTIONS-request 來確定CORS設定。max-age 定義快取的時間。預設為:1728000 (20天)。

http.cors.allow-methods
允許的http方法。預設為OPTIONS、HEAD、GET、POST、PUT、DELETE。

http.cors.allow-headers
允許的header。預設 X-Requested-With, Content-Type, Content-Length。

http.cors.allow-credentials
是否允許返回Access-Control-Allow-Credentials頭部。預設為:false。

http.detailed_errors.enabled
啟用或禁用輸出詳細的錯誤資訊和堆疊跟蹤響應輸出。預設為:true。

http.pipelining
啟用或禁用http管線化。預設為:true。

http.pipelining.max_events
一個http連線關閉之前最大記憶體中的時間佇列。預設為:10000。

Discovery部分

discovery.zen.minimum_master_nodes: 3
預防腦裂(split brain)通過配置大多數節點(總節點數/2+1)。預設為3。

discovery.zen.ping.multicast.enabled: false
設定是否開啟組播發現節點。預設false。

discovery.zen.ping.unicast.host
單播發現所使用的主機列表,可以設定一個屬組,或者以逗號分隔。每個值格式為 host:port 或 host(埠預設為:9300)。預設為 127.0.0.1,[::1]。

discovery.zen.ping.timeout: 3s
設定叢集中自動發現其它節點時ping連線超時時間,預設為3秒,對於比較差的網路環境可以高點的值來防止自動發現時出錯。

discovery.zen.join_timeout
節點加入到叢集中後,傳送請求到master的超時時間,預設值為ping.timeout的20倍。

discovery.zen.master_election.filter_client:true
當值為true時,所有客戶端節點(node.client:true或node.date,node.master值都為false)將不參加master選舉。預設值為:true。

discovery.zen.master_election.filter_data:false
當值為true時,不合格的master節點(node.data:true和node.master:false)將不參加選舉。預設值為:false。

discovery.zen.fd.ping_interval
傳送ping監測的時間間隔。預設為:1s。

discovery.zen.fd.ping_timeout
ping的響應超時時間。預設為30s。

discovery.zen.fd.ping_retries
ping監測失敗、超時的次數後,節點連線失敗。預設為3。

discovery.zen.publish_timeout
通過叢集api動態更新設定的超時時間,預設為30s。

discovery.zen.no_master_block
設定無master時,哪些操作將被拒絕。all 所有節點的讀、寫操作都將被拒絕。write 寫操作將被拒絕,可以讀取最後已知的叢集配置。預設為:write。

Gateway部分

gateway.expected_nodes: 0
設定這個叢集中節點的數量,預設為0,一旦這N個節點啟動,就會立即進行資料恢復。

gateway.expected_master_nodes
設定這個叢集中主節點的數量,預設為0,一旦這N個節點啟動,就會立即進行資料恢復。

gateway.expected_data_nodes
設定這個叢集中資料節點的數量,預設為0,一旦這N個節點啟動,就會立即進行資料恢復。

gateway.recover_after_time: 5m
設定初始化資料恢復程式的超時時間,預設是5分鐘。

gateway.recover_after_nodes
設定叢集中N個節點啟動時進行資料恢復。

gateway.recover_after_master_nodes
設定叢集中N個主節點啟動時進行資料恢復。

gateway.recover_after_data_nodes
設定叢集中N個資料節點啟動時進行資料恢復。

三.  Elasticsearch常用外掛

1)  elasticsearch-head 外掛
一個elasticsearch的叢集管理工具,它是完全由html5編寫的獨立網頁程式,你可以通過外掛把它整合到es。
專案地址:https://github.com/mobz/elasticsearch-head

外掛安裝方法1

elasticsearch/bin/plugin install mobz/elasticsearch-head
重啟elasticsearch
開啟http://localhost:9200/_plugin/head/

外掛安裝方法2

根據地址https://github.com/mobz/elasticsearch-head 下載zip解壓
建立elasticsearch/plugins/head/_site檔案
將解壓後的elasticsearch-head-master資料夾下的檔案copy到_site
重啟elasticsearch
開啟http://localhost:9200/_plugin/head/

2)  bigdesk外掛
elasticsearch的一個叢集監控工具,可以通過它來檢視es叢集的各種狀態,如:cpu、記憶體使用情況,索引資料、搜尋情況,http連線數等。
專案地址: https://github.com/hlstudio/bigdesk

外掛安裝方法1

elasticsearch/bin/plugin install hlstudio/bigdesk
重啟elasticsearch
開啟http://localhost:9200/_plugin/bigdesk/ 

外掛安裝方法2

https://github.com/hlstudio/bigdesk下載zip 解壓
建立elasticsearch-1.0.0\plugins\bigdesk\_site檔案
將解壓後的bigdesk-master資料夾下的檔案copy到_site
重啟elasticsearch
開啟http://localhost:9200/_plugin/bigdesk/ 

3)  Kopf 外掛
一個ElasticSearch的管理工具,它也提供了對ES叢集操作的API。
專案地址:https://github.com/lmenezes/elasticsearch-kopf

外掛安裝方法

elasticsearch/bin/plugin install lmenezes/elasticsearch-kopf
重啟elasticsearch
開啟http://localhost:9200/_plugin/kopf/

四.  Elasticsearch 的fielddata記憶體控制、預載入以及circuit breaker斷路器

fielddata核心原理
fielddata載入到記憶體的過程是lazy載入的,對一個analzyed field執行聚合時,才會載入,而且是field-level載入的一個index的一個field,所有doc都會被載入,而不是少數doc不是index-time建立,是query-time建立

fielddata記憶體限制
elasticsearch.yml: indices.fielddata.cache.size: 20%,超出限制,清除記憶體已有fielddata資料fielddata佔用的記憶體超出了這個比例的限制,那麼就清除掉記憶體中已有的fielddata資料預設無限制,限制記憶體使用,但是會導致頻繁evict和reload,大量IO效能損耗,以及記憶體碎片和gc

監控fielddata記憶體使用

#各個分片、索引的fielddata在記憶體中的佔用情況
[root@elk-node03 ~]# curl -X GET 'http://10.0.8.47:9200/_stats/fielddata?fields=*'     

#每個node的fielddata在記憶體中的佔用情況
[root@elk-node03 ~]# curl -X GET 'http://10.0.8.47:9200/_nodes/stats/indices/fielddata?fields=*'

#每個node中的每個索引的fielddata在記憶體中的佔用情況
[root@elk-node03 ~]# curl -X GET 'http://10.0.8.47:9200/_nodes/stats/indices/fielddata?level=indices&fields=*'

circuit breaker斷路由
如果一次query load的feilddata超過總記憶體,就會oom --> 記憶體溢位;
circuit breaker會估算query要載入的fielddata大小,如果超出總記憶體,就短路,query直接失敗;
在elasticsearch.yml檔案中配置如下內容:
indices.breaker.fielddata.limit: fielddata的記憶體限制,預設60%
indices.breaker.request.limit:  執行聚合的記憶體限制,預設40%
indices.breaker.total.limit:       綜合上面兩個,限制在70%以內

限制記憶體使用 (Elasticsearch聚合限制記憶體使用)

通常為了讓聚合(或者任何需要訪問欄位值的請求)能夠快點,訪問fielddata一定會快點, 這就是為什麼載入到記憶體的原因。但是載入太多的資料到記憶體會導致垃圾回收(gc)緩慢, 因為JVM試著發現堆裡面的額外空間,甚至導致OutOfMemory (即OOM)異常。

然而讓人吃驚的發現, Elaticsearch不是隻把符合你的查詢的值載入到fielddata. 而是把index裡的所document都載入到記憶體,甚至是不同的 _type 的document。邏輯是這樣的,如果你在這個查詢需要訪問documents X,Y和Z, 你可能在下一次查詢就需要訪問別documents。而一次把所有的值都載入並儲存在記憶體 , 比每次查詢都去掃描倒排索引要更方便。

JVM堆是一個有限制的資源需要聰明的使用。有許多現成的機制去限制fielddata對堆記憶體使用的影響。這些限制非常重要,因為濫用堆將會導致節點的不穩定(多虧緩慢的垃圾回收)或者甚至節點死亡(因為OutOfMemory異常);但是垃圾回收時間過長,在垃圾回收期間,ES節點的效能就會大打折扣,查詢就會非常緩慢,直到最後超時。

如何設定堆大小
對於環境變數 $ES_HEAP_SIZE 在設定Elasticsearch堆大小的時候有2個法則可以運用:

1) 不超過RAM的50%
Lucene很好的利用了檔案系統cache,檔案系統cache是由核心管理的。如果沒有足夠的檔案系統cache空間,效能就會變差;

2) 不超過32G
如果堆小於32GB,JVM能夠使用壓縮的指標,這會節省許多記憶體:每個指標就會使用4位元組而不是8位元組。把對記憶體從32GB增加到34GB將意味著你將有更少的記憶體可用,因為所有的指標佔用了雙倍的空間。同樣,更大的堆,垃圾回收變得代價更大並且可能導致節點不穩定;這個限制主要是大記憶體對fielddata影響比較大。

Fielddata大小
引數 indices.fielddata.cache.size 控制有多少堆記憶體是分配給fielddata。當你執行一個查詢需要訪問新的欄位值的時候,將會把值載入到記憶體,然後試著把它們加入到fielddata。如果結果的fielddata大小超過指定的大小 ,為了騰出空間,別的值就會被驅逐出去。預設情況下,這個引數設定的是無限制 — Elasticsearch將永遠不會把資料從fielddata裡替換出去。

這個預設值是故意選擇的:fielddata不是臨時的cache。它是一個在記憶體裡為了快速執行必須能被訪問的資料結構,而且構建它代價非常昂貴。如果你每個請求都要重新載入資料,效能就會很差。

一個有限的大小強迫資料結構去替換資料。下面來看看什麼時候去設定下面的值,首先看一個警告: 這個設定是一個保護措施,而不是一個記憶體不足的解決方案! 

如果你沒有足夠的記憶體區儲存你的fielddata到記憶體裡,Elasticsearch將會經常性的從磁碟重新載入資料,並且驅逐別的資料區騰出空間。這種資料的驅逐會導致嚴重的磁碟I/O,並且在記憶體裡產生大量的垃圾,這個會在後面被垃圾回收。

假設你在索引日誌,每天使用給一個新的索引。通常情況下你只會對過去1天或者2天的資料感興趣。即使你把老的索引資料保留著,你也很少查詢它們。儘管如此,使用預設的設定, 來自老索引的fielddata也不會被清除出去!fielddata會一直增長直到它觸發fielddata circuit breaker --參考斷路器--它將阻止你繼續載入fielddata。在那個時候你被卡住了。即使你仍然能夠執行訪問老的索引裡的fielddata的查詢, 你再也不能載入任何新的值了。相反,我們應該把老的值清除出去給新的值騰出空間。為了防止這種情景,通過在elasticsearch.yml檔案里加上如下的配置給fielddata 設定一個上限: indices.fielddata.cache.size:  40%   ,可以設定成堆大小的百分比,也可以是一個具體的值,比如 8gb;通過適當設定這個值,最近被訪問的fielddata將被清除出去,給新載入資料騰出空間。
 
在網上可能還會看到另外一個設定引數: indices.fielddata.cache.expire 千萬不要使用這個設定!這個設定高版本已經廢棄!!! 這個設定告訴Elasticsearch把比過期時間老的資料從fielddata裡驅逐出去,而不管這個值是否被用到。這對效能是非常可怕的 。驅逐資料是有代價的,並且這個有目的的高效的安排驅逐資料並沒有任何真正的收穫。沒有任何理由去使用這個設定!!!! 我們一點也不能從理論上製造一個假設的有用的情景。現階段存 在只是為了向後相容。我們在這個書裡提到這個設定是因為這個設定曾經在網路上的各種文章裡 被作為一個 ``效能小竅門'' 被推薦過。記住永遠不要使用它!!!!

監控fielddata (上面提到了)
監控fielddata使用了多少記憶體以及是否有資料被驅逐是非常重要的。大量的資料被驅逐會導致嚴重的資源問題以及不好的效能。

Fielddata使用可以通過下面的方式來監控:
對於單個索引使用 {ref}indices-stats.html[indices-stats API]:

[root@elk-node03 ~]# curl -X GET 'http://10.0.8.47:9200/_stats/fielddata?fields=

對於單個節點使用 {ref}cluster-nodes-stats.html[nodes-stats API]:

[root@elk-node03 ~]# curl -X GET 'http://10.0.8.47:9200/_nodes/stats/indices/fielddata?fields=*'

或者甚至單個節點單個索引

[root@elk-node03 ~]# curl -X GET 'http://10.0.8.47:9200/_nodes/stats/indices/fielddata?level=indices&fields=*'

通過設定 ?fields=* 記憶體使用按照每個欄位分解了.

斷路器(breaker)
fielddata的大小是在資料被載入之後才校驗的。如果一個查詢嘗試載入到fielddata的資料比可用的記憶體大會發生什麼情況?答案是不客觀的:你將會獲得一個OutOfMemory異常。

Elasticsearch包含了一個 fielddata斷路器 ,這個就是設計來處理這種情況的。斷路器通過檢查涉及的欄位(它們的型別,基數,大小等等)來估計查詢需要的記憶體。然後檢查加 載需要的fielddata會不會導致總的fielddata大小超過設定的堆的百分比。

如果估計的查詢大小超過限制,斷路器就會觸發並且查詢會被拋棄返回一個異常。這個發生在資料被載入之前,這就意味著你不會遇到OutOfMemory異常。

Elasticsearch擁有一系列的斷路器,所有的這些都是用來保證記憶體限制不會被突破:
indices.breaker.fielddata.limit
這個 fielddata 斷路器限制fielddata的大小為堆大小的60%,預設情況下。

indices.breaker.request.limit
這個 request 斷路器估算完成查詢的其他部分要求的結構的大小,比如建立一個聚集通, 以及限制它們到堆大小的40%,預設情況下。

indices.breaker.total.limit
這個total斷路器封裝了 request 和 fielddata 斷路器去確保預設情況下這2個 使用的總記憶體不超過堆大小的70%。

斷路器限制可以通過檔案 config/elasticsearch.yml 指定,也可以在叢集上動態更新:
curl -PUT 'http://10.0.8.47:9200/_cluster/settings{
"persistent" : {
"indices.breaker.fielddata.limit" : 40% (1)
}
}

這個限制設定的是堆的百分比。

最好把斷路器設定成一個相對保守的值。記住fielddata需要和堆共享 request 斷路器, 索引記憶體緩衝區,過濾器快取,開啟的索引的Lucene資料結構,以及各種各樣別的臨時資料 結構。所以預設為相對保守的60%。過分樂觀的設定可能會導致潛在的OOM異常,從而導致整 個節點掛掉。從另一方面來說,一個過分保守的值將會簡單的返回一個查詢異常,這個異常會被應用處理。 異常總比掛掉好。這些異常也會促使你重新評估你的查詢:為什麼單個的查詢需要超過60%的 堆空間。

斷路器和Fielddata大小
在 Fielddata大小部分我們談到了要給fielddata大小增加一個限制去保證老的不使用 的fielddata被驅逐出去。indices.fielddata.cache.size 和 indices.breaker.fielddata.limit 的關係是非常重要的。如果斷路器限制比緩衝區大小要小,就會沒有資料會被驅逐。為了能夠 讓它正確的工作,斷路器限制必須比緩衝區大小要大。

我們注意到斷路器是和總共的堆大小對比查詢大小,而不是和真正已經使用的堆記憶體區比較。 這樣做是有一系列技術原因的(比如,堆可能看起來是滿的,但是實際上可能正在等待垃圾 回收,這個很難準確的估算)。但是作為終端使用者,這意味著設定必須是保守的,因為它是 和整個堆大小比較,而不是空閒的堆比較。 

相關文章