@
ES應用場景
ES應用場景舉例
- 維基百科,類似百度百科,全文檢索,高亮,搜尋推薦
- The Guardian(國外新聞網站),使用者行為日誌(點選,瀏覽,收藏,評論)+社交網路資料,資料分析
- Stack Overflow(國外的程式異常討論論壇)
- GitHub(開原始碼管理)
- 電商網站,檢索商品
- 日誌資料分析,logstash採集日誌,ES進行復雜的資料分析(ELK技術,elasticsearch+logstash+kibana)
- 商品價格監控網站
- BI系統,商業智慧,Business Intelligence。
- 國內:站內搜尋(電商,招聘,門戶,等等),IT系統搜尋(OA,CRM,ERP,等等),資料分析(ES熱門的一個使用場景)
應用場景思考
ES的優勢在倒排索引的設計,以及單節點PB級資料儲存;所以有搜尋全文或大資料需求,可以考慮使用ES。
ES和其他資料庫對比
https://blog.csdn.net/weixin_39576751/article/details/112272464
ES架構
Gateway
儲存索引檔案的一個檔案系統且它支援很多型別,主要職責是對資料的持久化以及整個叢集重啟後可以通過gateway重新恢復資料
支援的格式有:本地Local FileSystem、分散式Shared FileSystem(做snapshot的時候會用到)、Hadoop的檔案系統HDFS、Amazon(亞馬遜)的S3。
Lucene
ES是基於Lucene的,一個分片就是一個lucene。 Lucene是做檢索的,但是他是一個單機的搜尋引擎,ES底層使用Lucene需要在每個節點上都執行Lucene進行響應的索引,查詢及更新所以需要支援業務層的分散式
資料處理
- Index Module:是索引模組,就是對資料建立索引也就是通常所說的建立一些倒排索引等
- Search Module:是搜尋模組,就是對資料進行查詢搜尋
- Mapping模組:是資料對映與解析模組,資料的每個欄位根據建立的表結構通過mapping進行對映解析,如果沒有建表結構,會根據資料型別推測資料結構自動生成一個mapping
- River模組:是執行在ES內部的一個外掛,主要用來從外部獲取異構資料,然後在ES裡建立索引
River常見的外掛有RabbitMQ River、Twitter River,例如可以通過一些自定義的指令碼將傳統的資料庫(mysql)等資料來源通過格式化轉換後直接同步到es叢集裡,這個river大部分是自己寫的,寫出來的東西質量參差不齊,將這些東西整合到es中會引發很多內部bug,嚴重影響了es的正常應用,所以在es2.0之後考慮將其去掉。
發現機制與指令碼
Elasticsearch是基於P2P的系統,它首先通過廣播的機制尋找存在的節點,然後再通過多播協議來進行節點間的通訊,同時也支援點對點的互動
- Directory 是ES自動發現節點的機制,Master選舉,預設使用的是 Zen,也可是使用EC2
- Scripting 是指令碼執行功能,有這個功能能很方便對查詢出來的資料進行加工處理
- 3rd Plugins 表示ES支援安裝很多第三方的外掛,例如elasticsearch-ik分詞外掛、elasticsearch-sql sql外掛。
Transport
ES支援多種通訊介面,Thrift、Memcached以及Http,預設的是http,JMX就是java的一個遠端監控管理框架
REST full API
ES暴露給我們的訪問介面,官方推薦的方案就是Restful介面,直接傳送http請求,方便後續使用nginx做代理、分發包括可能後續會做許可權的管理,通過http很容易做這方面的管理。如果使用java客戶端它是直接呼叫api,在做負載均衡以及許可權管理還是不太好做
ES中叢集、節點、索引、分片、段等概念
叢集
檢視叢集統計資訊(對大叢集非常有用),可以檢視jvm、os等資訊,進行調優或監控很有幫助,具體引數解釋檢視:https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-stats.html
GET _cluster/stats
{
"_nodes" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"cluster_uuid": "YjAvIhsCQ9CbjWZb2qJw3Q",
"cluster_name": "elasticsearch",
"timestamp": 1459427693515,
"status": "green",
"indices": {
"count": 1,
"shards": {
"total": 5,
"primaries": 5,
"replication": 0,
"index": {
"shards": {
"min": 5,
"max": 5,
"avg": 5
},
"primaries": {
"min": 5,
"max": 5,
"avg": 5
},
"replication": {
"min": 0,
"max": 0,
"avg": 0
}
}
},
"docs": {
"count": 10,
"deleted": 0
},
"store": {
"size": "16.2kb",
"size_in_bytes": 16684,
"total_data_set_size": "16.2kb",
"total_data_set_size_in_bytes": 16684,
"reserved": "0b",
"reserved_in_bytes": 0
},
"fielddata": {
"memory_size": "0b",
"memory_size_in_bytes": 0,
"evictions": 0
},
"query_cache": {
"memory_size": "0b",
"memory_size_in_bytes": 0,
"total_count": 0,
"hit_count": 0,
"miss_count": 0,
"cache_size": 0,
"cache_count": 0,
"evictions": 0
},
"completion": {
"size": "0b",
"size_in_bytes": 0
},
"segments": {
"count": 4,
"memory": "8.6kb",
"memory_in_bytes": 8898,
"terms_memory": "6.3kb",
"terms_memory_in_bytes": 6522,
"stored_fields_memory": "1.2kb",
"stored_fields_memory_in_bytes": 1248,
"term_vectors_memory": "0b",
"term_vectors_memory_in_bytes": 0,
"norms_memory": "384b",
"norms_memory_in_bytes": 384,
"points_memory" : "0b",
"points_memory_in_bytes" : 0,
"doc_values_memory": "744b",
"doc_values_memory_in_bytes": 744,
"index_writer_memory": "0b",
"index_writer_memory_in_bytes": 0,
"version_map_memory": "0b",
"version_map_memory_in_bytes": 0,
"fixed_bit_set": "0b",
"fixed_bit_set_memory_in_bytes": 0,
"max_unsafe_auto_id_timestamp" : -9223372036854775808,
"file_sizes": {}
},
"mappings": {
"field_types": [],
"runtime_field_types": []
},
"analysis": {
"char_filter_types": [],
"tokenizer_types": [],
"filter_types": [],
"analyzer_types": [],
"built_in_char_filters": [],
"built_in_tokenizers": [],
"built_in_filters": [],
"built_in_analyzers": []
},
"versions": [
{
"version": "8.0.0",
"index_count": 1,
"primary_shard_count": 1,
"total_primary_size": "7.4kb",
"total_primary_bytes": 7632
}
]
},
"nodes": {
"count": {
"total": 1,
"data": 1,
"coordinating_only": 0,
"master": 1,
"ingest": 1,
"voting_only": 0
},
"versions": [
"7.14.0"
],
"os": {
"available_processors": 8,
"allocated_processors": 8,
"names": [
{
"name": "Mac OS X",
"count": 1
}
],
"pretty_names": [
{
"pretty_name": "Mac OS X",
"count": 1
}
],
"architectures": [
{
"arch": "x86_64",
"count": 1
}
],
"mem" : {
"total" : "16gb",
"total_in_bytes" : 17179869184,
"free" : "78.1mb",
"free_in_bytes" : 81960960,
"used" : "15.9gb",
"used_in_bytes" : 17097908224,
"free_percent" : 0,
"used_percent" : 100
}
},
"process": {
"cpu": {
"percent": 9
},
"open_file_descriptors": {
"min": 268,
"max": 268,
"avg": 268
}
},
"jvm": {
"max_uptime": "13.7s",
"max_uptime_in_millis": 13737,
"versions": [
{
"version": "12",
"vm_name": "OpenJDK 64-Bit Server VM",
"vm_version": "12+33",
"vm_vendor": "Oracle Corporation",
"bundled_jdk": true,
"using_bundled_jdk": true,
"count": 1
}
],
"mem": {
"heap_used": "57.5mb",
"heap_used_in_bytes": 60312664,
"heap_max": "989.8mb",
"heap_max_in_bytes": 1037959168
},
"threads": 90
},
"fs": {
"total": "200.6gb",
"total_in_bytes": 215429193728,
"free": "32.6gb",
"free_in_bytes": 35064553472,
"available": "32.4gb",
"available_in_bytes": 34802409472
},
"plugins": [
{
"name": "analysis-icu",
"version": "7.14.0",
"description": "The ICU Analysis plugin integrates Lucene ICU module into elasticsearch, adding ICU relates analysis components.",
"classname": "org.elasticsearch.plugin.analysis.icu.AnalysisICUPlugin",
"has_native_controller": false
},
...
],
"ingest": {
"number_of_pipelines" : 1,
"processor_stats": {
...
}
},
"network_types": {
...
},
"discovery_types": {
...
},
"packaging_types": [
{
...
}
]
}
}
檢視叢集狀態,可以更細到索引層面,檢視具體哪個索引導致叢集狀態Red等。
GET _cluster/health?level=indices
GET _cluster/health?level=shards
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_cluster_health.html
節點
es叢集最大節點建議200。過多節點會產生什麼問題?
對於每個 Elasticsearch 索引,對映和狀態的相關資訊都儲存在叢集狀態中。這些資訊儲存在記憶體中,以便快速訪問。因此,如果叢集中的索引和分片數量過多,這會導致叢集狀態過大,如果對映較大的話,尤為如此。這會導致更新變慢,因為所有更新都需要通過單執行緒完成,從而在將變更分發到整個叢集之前確保一致性
叢集狀態會載入到每個節點(包括主節點)上的堆記憶體中,而且堆記憶體大小與索引數量以及單個索引和分片中的欄位數成正比關係,所以還需要同時監測主節點上的堆記憶體使用量並確保其大小適宜,這一點很重要。
GET /_nodes/stats # 同上叢集
索引
索引相當於資料庫中Database。每個索引又由一個或多個分片組成。每個分片都是一個 Lucene 索引例項,您可以將其視作一個獨立的搜尋引擎,它能夠對 Elasticsearch 叢集中的資料子集進行索引並處理相關查詢。
Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields
分片
一個索引預設1000分片(引數:cluster.max_shards_per_node)。
人們通常將 50GB 作為分片上限,而且這一限值在各種用例中都已得到驗證
這裡有一個很好的經驗法則:確保對於節點上已配置的每個 GB,將分片數量保持在 20 以下。如果某個節點擁有 30GB 的堆記憶體,那其最多可有 600 個分片,但是在此限值範圍內,您設定的分片數量越少,效果就越好。一般而言,這可以幫助叢集保持良好的執行狀態。
GET /_cat/shards
https://www.elastic.co/cn/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster
段(segment)
GET /_cat/segments?v
index shard prirep ip segment generation docs.count docs.deleted size size.memory committed searchable version compound
test 0 p 127.0.0.1 _0 0 1 0 3kb 2042 false true 8.0.0 true
test1 0 p 127.0.0.1 _0 0 1 0 3kb 2042 false true 8.0.0 true
GET _segments # 可檢視每個段資訊
已提交索引段是指那些已經執行過提交命令的段,意味著在磁碟已經持久化,且是隻讀的
文件(document)
一個文件是一個可被索引的基礎資訊單元。儲存的資料是文件,一條資料對應一篇文件,類似mysql資料庫中的一行資料
欄位(field)
一個文件中對應的多個列,類似mysql資料庫中一行有多列。欄位包括詞項和詞項對應的value值
詞項(term)
詞項是索引的最小單位,一般可以把詞項當作詞
詞條(token)(_analyze查詢)
查詢token:https://www.elastic.co/guide/en/elasticsearch/reference/1.6/docs-termvectors.html#_term_information
GET /_analyze?
{
"analyzer": "standard",
"text": "orJ2t4r8Rlgz-988Y947mMas5zuU"
}
返回:
{
"tokens": [
{
"token": "orj2t4r8rlgz",
"start_offset": 0,
"end_offset": 12,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "988y947mmas5zuu",
"start_offset": 13,
"end_offset": 28,
"type": "<ALPHANUM>",
"position": 1
}
]
}
Mapping
可以理解為mysql或者solr中對應的schema,只不過這裡的mapping增加了動態識別功能
分片解決什麼問題?
允許集群系統儲存資料總量超過單機容量
分片數該怎麼設定?
預設5分片是標準用法。
如果有一個有限且明確的資料集——可以只使用一個分片。
如果沒有,最理想分片數應該依賴於節點的數量:
節點最大數 = 分片數 *(副本數 + 1)
節點最大數該如何確定?
副本解決什麼問題?
解決訪問壓力過大時,單機無法處理所有請求,應對增長的併發查詢。
缺點:各分片副本佔用額外的儲存空間,主分片及副本之間資料拷貝也存在時間開銷。
叢集的初期規劃
單叢集最多節點200
單節點最多600分片,es預設每個節點最多1000分片
確保對於節點上已配置的每個 GB,將分片數量保持在 20 以下。如果某個節點擁有 30GB 的堆記憶體,那其最多可有 600 個分片,但是在此限值範圍內,您設定的分片數量越少,效果就越好
單分片大小控制在50GB
最後根據自己要儲存的資料量,進行壓測。再根據壓測資料,對索引設定分片大小。
建立一定量資料後,可:
GET _cluster/stats # 檢視叢集狀態(os、jvm、indices、nodes等資訊)
GET /_nodes/stats # 同上叢集
GET /_cat/shards # 檢視索引分片
GET /_cat/segments # 檢視segments
ES底層資料結構
Elasticsearch Server(Elasticsearch caches)
https://elastic.blog.csdn.net/article/details/108970774
un-inverted data(Doc Values 正排索引)
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_deep_dive_on_doc_values.html
場景
Elasticsearch 中的 Doc Values 常被應用到以下場景:
- 對一個欄位進行排序
- 對一個欄位進行聚合
- 某些過濾,比如地理位置過濾
- 某些與欄位相關的指令碼計算
生成
Doc Values 是在索引時與 倒排索引 同時生成。也就是說 Doc Values 和 倒排索引 一樣,基於 Segement 生成並且是不可變的。同時 Doc Values 和 倒排索引 一樣序列化到磁碟,這樣對效能和擴充套件性有很大幫助。
因為文件值被序列化到磁碟,我們可以依靠作業系統的幫助來快速訪問。
當 工作集(working set) 遠小於節點的可用記憶體,系統會自動將所有的文件值儲存在記憶體中,使得其讀寫十分高速;
當其遠大於可用記憶體,作業系統會自動把 Doc Values 載入到系統的頁快取中,從而避免了 jvm 堆記憶體溢位異常
禁用Doc Values
Doc Values 預設對所有欄位啟用,除了 analyzed strings。也就是說所有的數字、地理座標、日期、IP 和不分析( not_analyzed )字元型別都會預設開啟。
analyzed strings 暫時還不能使用 Doc Values。文字經過分析流程生成很多 Token,使得 Doc Values 不能高效執行
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"session_id": {
"type": "string",
"index": "not_analyzed",
"doc_values": false
}
}
}
}
}
列式儲存
列儲存優勢:
- 自動索引
因為基於列儲存,所以每一列本身就相當於索引。所以在做一些需要索引的操作時,就不需要額外的資料結構來為此列建立合適的索引。
- 利於資料壓縮
利於壓縮有兩個原因。一來你會發現大部分列資料基數其實是重複的,二來相同的列資料型別一致,這樣利於資料結構填充的優化和壓縮,而且對於數字列這種資料型別可以採取更多有利的演算法去壓縮儲存。
https://blog.csdn.net/dc_726/article/details/41143175
inverted data(倒排索引)
倒排索引特點
- 在索引時建立
- 序列化到磁碟
- 全文搜尋非常快
- 不適合做排序
- 預設開啟
倒排索引適用場景
- 查詢
- 全文檢索
倒排索引資料結構
文字欄位儲存在倒排索引中,數字欄位和地理欄位儲存在BKD樹中。
資料型別 | 資料結構 |
---|---|
text/keyword | 倒排索引 |
數字/地理位置 | BKD樹 |
ES記憶體
es 記憶體由哪些東西佔用
https://nereuschen.github.io/2015/09/16/ElasticSearch記憶體使用分析/
https://blog.csdn.net/jiao_fuyou/article/details/50509941
由於叢集狀態會載入到每個節點(包括主節點)上的堆記憶體中,而且堆記憶體大小與索引數量以及單個索引和分片中的欄位數成正比關係,所以還需要同時監測主節點上的堆記憶體使用量並確保其大小適宜,這一點很重要。
每個分片都有一部分資料需要儲存在記憶體中,這部分資料也會佔用堆記憶體空間。這包括儲存分片級別以及段級別資訊的資料結構,因為只有這樣才能確定資料在磁碟上的儲存位置。這些資料結構的大小並不固定,不同用例之間會有很大的差別
為了能夠在單個節點上儲存儘可能多的資料,下面兩點至關重要:管理堆記憶體使用量;儘可能減少開銷。節點的堆記憶體空間越多,其能處理的資料和分片就越多。
(https://www.elastic.co/cn/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster)
https://www.elastic.co/cn/blog/significantly-decrease-your-elasticsearch-heap-memory-usage
https://blog.csdn.net/qq_42046105/article/details/91488572?ops_request_misc=%7B%22request%5Fid%22%3A%22162900587816780264061739%22%2C%22scm%22%3A%2220140713.130102334.pc%5Fblog.%22%7D&request_id=162900587816780264061739&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_v2~rank_v29-3-91488572.pc_v2_rank_blog_default&utm_term=elasticsearch&spm=1018.2226.3001.4450
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_finding_exact_values.html#_internal_filter_operation
https://www.elastic.co/guide/cn/elasticsearch/guide/current/filter-caching.html
https://blog.csdn.net/jiao_fuyou/article/details/50509941
https://segmentfault.com/a/1190000040238635?utm_source=sf-similar-article
query_cache
Query Cache也稱為Filter Cache,顧名思義它的作用就是對一個查詢中包含的過濾器執行結果進行快取。
比如我們常用的term,terms,range過濾器都會在滿足某種條件後被快取,注意,這裡的bool過濾器是不會被快取的,但bool過濾器包含的子query clause會被快取
(5.1.1版本term被取消了)
query_cache總結
- Query Cache是節點級別的,每個節點上的所有分片共享一份快取
- Query Cache採用LRU快取失效策略
- Query Cache只能在Filter Context中被使用
- Query Cache可以通過 indices.queries.cache.size來設定快取佔用大小,預設10%,可手動設定比如512mb
- Query Cache實際快取的是Bitset(點陣圖),一個Query clause對應一個Bitset
- 快取要生效,必須滿足兩個條件
a)查詢對應的segments所持有的文件數必須大於10000
b)查詢對應的segments所持有的文件數必須大於整個索引size的3% - 當新索引文件時,Query Cache不會重新計算,而是判斷索引的文件是否符合快取對應的Query clause,滿足則加入BitSet中
https://blog.csdn.net/chennanymy/article/details/52403594
https://segmentfault.com/a/1190000040238635?utm_source=sf-similar-article
過濾器內部原理
- 查詢匹配文件.
term 查詢在倒排索引中查詢 XHDK-A-1293-#fJ3 然後獲取包含該 term 的所有文件。本例中,只有文件 1 滿足我們要求。
- 建立 bitset.
過濾器會建立一個 bitset (一個包含 0 和 1 的陣列),它描述了哪個文件會包含該 term 。匹配文件的標誌位是 1 。本例中,bitset 的值為 [1,0,0,0] 。在內部,它表示成一個 "roaring bitmap",可以同時對稀疏或密集的集合進行高效編碼。
- 迭代 bitset(s)
一旦為每個查詢生成了 bitsets ,Elasticsearch 就會迴圈迭代 bitsets 從而找到滿足所有過濾條件的匹配文件的集合。執行順序是啟發式的,但一般來說先迭代稀疏的 bitset (因為它可以排除掉大量的文件)。
- 增量使用計數.
Elasticsearch 能夠快取非評分查詢從而獲取更快的訪問,但是它也會不太聰明地快取一些使用極少的東西。非評分計算因為倒排索引已經足夠快了,所以我們只想快取那些我們 知道 在將來會被再次使用的查詢,以避免資源的浪費。
為了實現以上設想,Elasticsearch 會為每個索引跟蹤保留查詢使用的歷史狀態。如果查詢在最近的 256 次查詢中會被用到,那麼它就會被快取到記憶體中。當 bitset 被快取後,快取會在那些低於 10,000 個文件(或少於 3% 的總索引數)的段(segment)中被忽略。這些小的段即將會消失,所以為它們分配快取是一種浪費。
https://www.elastic.co/guide/cn/elasticsearch/guide/current/filter-caching.html
request_cache
全稱是 Shard Request Cache,即分片級請求快取。當對一個或多個索引發送搜尋請求時,搜尋請求首先會發送到ES叢集中的某個節點,稱之為協調節點;協調節點會把該搜尋請求分發給其他節點並在相應分片上執行搜尋操作,我們把分片上的執行結果稱為“本地結果集”,之後,分片再將執行結果返回給協調節點;協調節點獲得所有分片的本地結果集之後,合併成最終的結果並返回給客戶端。
Request Cache 在每個分片上快取了本地結果集,這使得頻繁使用的搜尋請求幾乎立即返回結果。預設情況下只會快取查詢中引數 size=0 的搜尋請求的結果,因此將不會快取hits,但會快取 hits.total,aggregations(聚合) 和 suggestions。所以,request cache 分片請求快取非常適合日誌用例場景,在這種情況下,資料不會在舊索引上更新,並且可以將常規聚合保留在快取記憶體中以供重用。
https://blog.csdn.net/a745233700/article/details/118055871
fielddata
從歷史上看,fielddata 是 所有 欄位的預設設定。但是 Elasticsearch 已遷移到 doc values 以減少 OOM 的機率(ES2.0)。分析的字串是仍然使用 fielddata 的最後一塊陣地。 最終目標是建立一個序列化的資料結構類似於 doc values ,可以處理高維度的分析字串,逐步淘汰 fielddata。
fielddata 需要與 request 斷路器共享堆記憶體、索引緩衝記憶體和過濾器快取
可配置:indices.fielddata.cache.size
GET /_nodes/stats/indices/fielddata
fielddata 特點
- 適用於文件之類的操作,但僅適用於 text 文字欄位型別
- 在查詢時建立
- 記憶體中資料結構
- 沒有序列化到磁碟
- 預設情況下被禁用(構建它們很昂貴,並且在堆中預置)
fielddata 適用場景
- 全文統計詞頻
- 全文生成詞雲
- text型別:聚合、排序、指令碼計算
fielddata 使用注意事項
在啟用欄位資料之前,請考慮為什麼將文字欄位用於聚合、排序或在指令碼中使用。啟用 fielddata 通常沒有任何意義,因為它非常耗費記憶體資源。僅僅是做全文搜尋的應用,就不需要啟用fielddata。
https://blog.csdn.net/laoyang360/article/details/108970774
https://www.elastic.co/guide/cn/elasticsearch/guide/current/aggregations-and-analysis.html
Elasticsearch Server(Elasticsearch caches)
參考:
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_talking_to_elasticsearch.html
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-create-index.html
Indexing Buffer
Indexing Buffer是用來快取新資料,當其滿了或者refresh/flush interval到了,就會以segment file的形式寫入到磁碟。 這個引數的預設值是10% heap size。根據經驗,這個預設值也能夠很好的工作,應對很大的索引吞吐量。 但有些使用者認為這個buffer越大吞吐量越高,因此見過有使用者將其設定為40%的。到了極端的情況,寫入速度很高的時候,40%都被佔用,導致OOM。
Cluster State Buffer
ES被設計成每個node都可以響應使用者的api請求,因此每個node的記憶體裡都包含有一份叢集狀態的拷貝。這個cluster state包含諸如叢集有多少個node,多少個index,每個index的mapping是什麼?有少shard,每個shard的分配情況等等 (ES有各類stats api獲取這類資料)。 在一個規模很大的叢集,這個狀態資訊可能會非常大的,耗用的記憶體空間就不可忽視了。並且在ES2.0之前的版本,state的更新是由master node做完以後全量散播到其他結點的。 頻繁的狀態更新都有可能給heap帶來壓力。 在超大規模叢集的情況下,可以考慮分叢集並通過tribe node連線做到對使用者api的透明,這樣可以保證每個叢集裡的state資訊不會膨脹得過大。
超大搜索聚合結果集的fetch
ES是分散式搜尋引擎,搜尋和聚合計算除了在各個data node平行計算以外,還需要將結果返回給彙總節點進行彙總和排序後再返回。無論是搜尋,還是聚合,如果返回結果的size設定過大,都會給heap造成很大的壓力,特別是資料匯聚節點。超大的size多數情況下都是使用者用例不對,比如本來是想計算cardinality,卻用了terms aggregation + size:0這樣的方式; 對大結果集做深度分頁;一次性拉取全量資料等等。
為什麼記憶體設定不要超過32G
Java 使用一個叫作 記憶體指標壓縮(compressed oops)的技術來解決這個問題。 它的指標不再表示物件在記憶體中的精確位置,而是表示 偏移量 。這意味著 32 位的指標可以引用 40 億個 物件 , 而不是 40 億個位元組。最終, 也就是說堆記憶體增長到 32 GB 的實體記憶體,也可以用 32 位的指標表示。
一旦你越過那個神奇的 ~32 GB 的邊界,指標就會切回普通物件的指標。 每個物件的指標都變長了,就會使用更多的 CPU 記憶體頻寬,也就是說你實際上失去了更多的記憶體。事實上,當記憶體到達 40–50 GB 的時候,有效記憶體才相當於使用記憶體物件指標壓縮技術時候的 32 GB 記憶體。
這段描述的意思就是說:即便你有足夠的記憶體,也儘量不要 超過 32 GB。因為它浪費了記憶體,降低了 CPU 的效能,還要讓 GC 應對大記憶體。
( 大記憶體的機器、及swapping)
https://www.elastic.co/guide/cn/elasticsearch/guide/current/heap-sizing.html
ES語句執行過程
https://www.elastic.co/guide/cn/elasticsearch/guide/current/distrib-write.html
es 寫資料過程
倒排索引寫過程
https://www.elastic.co/guide/cn/elasticsearch/guide/current/translog.html
es 讀資料過程
es 查詢資料過程
- 客戶端傳送請求到一個 coordinate node 。
- 協調節點將搜尋請求轉發到所有的 shard 對應的 primary shard 或 replica shard ,都可以。
- query phase:每個 shard 將自己的搜尋結果(其實就是一些 doc id )返回給協調節點,由協調節點進行資料的合併、排序、分頁等操作,產出最終結果。
- fetch phase:接著由協調節點根據 doc id 去各個節點上拉取實際的 document 資料,最終返回給客戶端。
所有的查詢都或多或少的會進行相關度計算,但不是所有的查詢都會有分析階段,文字查詢可以分為兩個部分:
1. 基於詞項的查詢,如 term 或 fuzzy 這樣的查詢是沒有分析階段的。他們對單個詞項進行操作。
2. 基於全文的查詢,比如match, 它們會先了解欄位對映的資訊,判斷欄位是否被分詞,是否是日期還是數字等, 再根據對映資訊,構建要查詢的詞項列表,根據列表進行查詢。
https://www.jianshu.com/p/6d55b24fb9ab
段合併
https://www.elastic.co/guide/cn/elasticsearch/guide/current/dynamic-indices.html
ES分散式架構
分散式架構特性
透明隱藏
- 分片機制:我們不用關心資料是按照什麼機制分片的、最後放入到哪個分片中
- 分片副本:實現資料的高可用與負載均衡
- 叢集發現機制(cluster discovery):比如當前我們啟動了一個es程序,當啟動了第二個es程序時,這個程序作為一個node自動就發現了叢集,並且加入了進去
- shard負載均衡:比如現在有10shard,叢集中有3個節點,es會進行均衡的進行分配,以保持每個節點均衡的負載請求
- 請求路由
- 叢集擴容:垂直擴容-替換低配置機器;水平擴容-直接增加新的機器
- Shard重分配
rebalance
增加或者減少機器時候會自動均衡
master節點
主節點的主要職責是和叢集操作相關的內容,如建立或刪除索引,跟蹤哪些節點是群集的一部分,並決定哪些分片分配給相關的節點。穩定的主節點對叢集的健康是非常重要的
節點對等
- 每個節點都能接收請求
- 每個節點接收到請求後都能把該請求路由到有相關資料的其它節點上
接收原始請求的節點負責採集資料並返回給客戶端
叢集節點型別
- master:可競選主節點
- data:資料節點負責資料的儲存和相關具體操作,比如CRUD、搜尋、聚合
- ingest:提取節點。Ingest node可以通過ingest pipeline對文件執行預處理操作,以便在索引文件之前對文件進行轉換或者增強。
- ml(Machine learning node):機器學習
- remote_cluster_client
- transform
https://www.elastic.co/guide/en/elasticsearch/reference/7.9/modules-node.html
Zen發現
Zen是Elasticsearch預設發現機制。Zen發現預設使用多播來發現其他節點。有時多播由於各種原因而失效,或在一個大型叢集多播發現產生大量不必要流量Zen也可使用單播方式。
最小節點數
discovery.zen.minimum_master_nodes = 候選主節點(叢集節點數)/ 2 + 1
Zen發現錯誤檢測
- 第一個流程,由主節點向其他節點發送ping請求檢測是否正常
- 第二個流程,由每個節點向主節點發送請求來驗證主節點是否在執行並履行其職責。
Master選舉
https://zhuanlan.zhihu.com/p/34858035
master選舉誰發起,什麼時候發起?
master選舉當然是由master-eligible節點發起,當一個master-eligible節點發現滿足以下條件時發起選舉:
- 該master-eligible節點的當前狀態不是master。
- 該master-eligible節點通過ZenDiscovery模組的ping操作詢問其已知的叢集其他節點,沒有任何節點連線到master。
- 包括本節點在內,當前已有超過minimum_master_nodes個節點沒有連線到master。
總結一句話,即當一個節點發現包括自己在內的多數派的master-eligible節點認為叢集沒有master時,就可以發起master選舉。
選舉過程
假如叢集中有3個master-eligible node,分別為Node_A、 Node_B、 Node_C, 選舉優先順序也分別為Node_A、Node_B、Node_C。三個node都認為當前沒有master,於是都各自發起選舉,選舉結果都為Node_A(因為選舉時按照優先順序排序,如上文所述)。於是Node_A開始等join(選票),Node_B、Node_C都向Node_A傳送join,當Node_A接收到一次join時,加上它自己的一票,就獲得了兩票了(超過半數),於是Node_A成為Master。此時cluster_state(叢集狀態)中包含兩個節點,當Node_A再收到另一個節點的join時,cluster_state包含全部三個節點。
https://zhuanlan.zhihu.com/p/334348919
腦裂
腦裂問題可能有以下幾個原因造成:
- 網路問題:叢集間的網路延遲導致一些節點訪問不到Master,認為Master 掛掉了,而master其實並沒有宕機,而選舉出了新的Master,並對Master上的分片和副本標紅,分配新的主分片。
- 節點負載:主節點的角色既為Master又為Data,訪問量較大時可能會導致 ES 停止響應(假死狀態)造成大面積延遲,此時其他節點得不到主節點的響應認為主節點掛掉了,會重新選取主節點。
- 記憶體回收:主節點的角色既為Master又為Data,當Data節點上的ES程序佔用的記憶體較大,引發JVM的大規模記憶體回收,造成ES程序失去響應。
如何避免腦裂:我們可以基於上述原因,做出優化措施:
- 適當調大響應超時時間,減少誤判。通過引數 discovery.zen.ping_timeout 設定節點ping超時時間,預設為 3s,可以適當調大。
- 選舉觸發,我們需要在候選節點的配置檔案中設定引數 discovery.zen.munimum_master_nodes 的值。這個引數表示在選舉主節點時需要參與選舉的候選主節點的節點數,預設值是 1,官方建議取值(master_eligibel_nodes/2)+1,其中 master_eligibel_nodes 為候選主節點的個數。這樣做既能防止腦裂現象的發生,也能最大限度地提升叢集的高可用性,因為只要不少於 discovery.zen.munimum_master_nodes 個候選節點存活,選舉工作就能正常進行。當小於這個值的時候,無法觸發選舉行為,叢集無法使用,不會造成分片混亂的情況。
- 角色分離,即是上面我們提到的候選主節點和資料節點進行角色分離,這樣可以減輕主節點的負擔,防止主節點的假死狀態發生,減少對主節點宕機的誤判。
Allocation&Rebalance
https://www.jianshu.com/p/a65c9c62db4a
https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-cluster.html
shard allocation發生時機
- 建立/刪除一個Index
- 加入/離開一個Node
- 手動執行reroute命令
- 修改replication數量
- 叢集重啟
- 當把allocation關閉的時候,新建的index的shard不會被建立!
- 手動reroute不受allocation關閉影響。
- 節點重啟時恢復上面的primary shard不受allocation的配置是否開啟的影響,但是replica shared不會被重新allocation上來。
- allocation關閉以後,可以使用reroute指令手動顯示進行allocation,不受影響。
allocation 過程
- allocator:嘗試尋找最優的節點來分配分片
- 對於新建的索引。allocator按照分片數量升序排序節點列表,注意,是按照分片數量,而不是分片的大小 。然後decider依次遍歷節點列表,根據分配規則判斷是否要把分片分配到該節點。
- 對於已有的索引。當節點重啟時,觸發對於已有分片的分配。對於主分片,allocator只允許把主分片分配到擁有分片完整資料的節點上,即只有完整資料的副分片才能升為主節點;對於副分片,allocator優先分配到擁有分片資料(即使資料不是最新的)的節點上。
- decider:負責判斷並決定是否要進行要分配
allocation與rebalance的決策權由下列有控制的decider決定,只有全返回true,才可allocation/rebalance(如果沒控制,預設返回true。例如SameShardAllocationDecider不控制rebalance,則返回true)
rebalance
通過閾值判定,將在不同的node上的shard轉移來平衡叢集中每臺node的shard數。發生在節點增減、修改replica的配置的時候。
關閉allocation以後,rebalance過程也會被禁止。
資料擴容
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_scale_horizontally.html
資料遷移
https://cloud.tencent.com/developer/article/1825511?from=article.detail.1529651
https://help.aliyun.com/document_detail/170095.html
資料恢復
nested
nested 的可能的效能問題不容小覷。
nested本質:每個巢狀物件都被索引為一個單獨的Lucene文件。如果我們為包含100個使用者物件的單個文件建立索引,則將建立101個Lucene文件。
nested 較 父子文件不同之處:
- 如果子文件頻繁更新,建議使用父子文件。
- 如果子文件不頻繁更新,查詢頻繁建議 nested型別
評分
查詢和過濾
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_queries_and_filters.html
從 Elasticsearch 2.0 開始,過濾(filters)已經從技術上被排除了,同時所有的查詢(queries)擁有變成不評分查詢的能力。
然而,為了明確和簡單,我們用 "filter" 這個詞表示不評分、只過濾情況下的查詢。你可以把 "filter" 、 "filtering query" 和 "non-scoring query" 這幾個詞視為相同的。
相似的,如果單獨地不加任何修飾詞地使用 "query" 這個詞,我們指的是 "scoring query" 。
過濾查詢(Filtering queries)只是簡單的檢查包含或者排除,這就使得計算起來非常快。考慮到至少有一個過濾查詢(filtering query)的結果是 “稀少的”(很少匹配的文件),並且經常使用不評分查詢(non-scoring queries),結果會被快取到記憶體中以便快速讀取,所以有各種各樣的手段來優化查詢結果。
相反,評分查詢(scoring queries)不僅僅要找出匹配的文件,還要計算每個匹配文件的相關性,計算相關性使得它們比不評分查詢費力的多。同時,查詢結果並不快取。
相關性
https://www.elastic.co/guide/cn/elasticsearch/guide/current/relevance-intro.html
https://www.elastic.co/guide/cn/elasticsearch/guide/current/controlling-relevance.html
https://www.elastic.co/guide/cn/elasticsearch/guide/current/scoring-theory.html
檢索詞頻率:檢索詞在該欄位出現的頻率?出現頻率越高,相關性也越高。 欄位中出現過 5 次要比只出現過 1 次的相關性高。
反向文件頻率:每個檢索詞在索引中出現的頻率?頻率越高,相關性越低。檢索詞出現在多數文件中會比出現在少數文件中的權重更低。
欄位長度準則:欄位的長度是多少?長度越長,相關性越低。 檢索詞出現在一個短的 title 要比同樣的詞出現在一個長的 content 欄位權重更大
https://en.wikipedia.org/wiki/Okapi_BM25
https://en.wikipedia.org/wiki/Tf–idf
https://www.elastic.co/guide/en/elasticsearch/reference/current/similarity.html
https://www.jianshu.com/p/70d1c3045c11
調優
硬體環境選擇
如果有條件,儘可能使用SSD硬碟, 不錯的CPU。ES的厲害之處在於ES本身的分散式架構以及lucene的特性。IO的提升,會極大改進ES的速度和效能。
初期規劃
初期規劃,設定合適的分片數,叢集節點數。
使用alias:生產提供服務的索引,切記使用別名提供服務,而不是直接暴露索引名稱,避免後續因為業務變更或者索引資料需要reindex等情況造成業務中斷。
冷熱資料隔離(如日誌資料)
避免寬表
在索引中定義太多欄位是一種可能導致對映爆炸的情況,這可能導致記憶體不足錯誤和難以恢復的情況,這個問題可能比預期更常見,index.mapping.total_fields.limit ,預設值是1000
避免稀疏索引
因為索引稀疏之後,對應的相鄰文件id的delta值會很大,lucene基於文件id做delta編碼壓縮導致壓縮率降低,從而導致索引檔案增大,同時,es的keyword,陣列型別採用doc_values結構,每個文件都會佔用一定的空間,即使欄位是空值,所以稀疏索引會造成磁碟size增大,導致查詢和寫入效率降低。
作者:架構文摘
連結:https://juejin.cn/post/6868081322779230216
來源:掘金
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
系統層面的調優
系統層面的調優主要是記憶體的設定與避免交換記憶體。
記憶體調優
- 垃圾回收器選擇
- 新生代老年代比例設定,有無記憶體洩漏
引數調優
- 配置項cluster.routing.allocation.node_concurrent_recoveries 決定了單個節點執行副分片recovery時最大的併發數,預設為2,可以適當提高。
- 配置項indices.recovery.max_bytes_per_sec決定節點間複製資料時的限速,預設為40MB,可以適當提高。
- 配置項cluster.routing.allocation.node_initial_primaries_recoveries決定了單個節點執行主分片recovery時的最大併發數,預設為4。可以適當提高。
- 在重啟叢集前,停止寫入並執行sync flush,恢復時有機會跳過phase1。
適當多保留些translog,配置項index.translog.retention.size預設最大保留512MB,index.translog.retention.age預設12小時,調整這兩個引數可以有機會跳過phase1 - 合併Lucene分段,對於冷索引執行_forcemerge,較少的分段可以提升恢復效率。
寫入速度優化
綜合來說,可以考慮以下幾個方面來提升寫索引的效能:
- 加大 Translog Flush ,目的是降低 Iops、Writeblock。
- 增加 Index Refresh 間隔,目的是減少 Segment Merge 的次數。
- 調整 Bulk 執行緒池和佇列。
- 優化節點間的任務分佈。
- 優化 Lucene 層的索引建立,目的是降低 CPU 及 IO。
https://cloud.tencent.com/developer/article/1436787
translog flush間隔調整
在預設設定下,translog的持久化策略為每個請求都flush,這個是影響es寫入速度的最大因素,如果可以接受一定概率的資料丟失,可以將刷盤設定為週期性或者到一定大小再刷盤。索引重新整理間隔refresh_interval
預設索引的refresh_interval為1秒,這意味著資料寫入1秒後就可以搜尋到,每次索引的refresh會產生一個新的Lucene段,這會導致頻繁的segment merge行為,如果不需要這麼高的搜尋實時性,應該降低索引refresh週期。使用bulk請求
批量寫比一個索引請求只寫單個文件的效率高得多。自動生成doc ID
寫入文件時如果外部指定了id,則es會先嚐試讀取原來的doc版本號,以判斷是否要更新,這會涉及一次讀磁碟的操作,通過自動生成doc id可以避免。減少副本的數量
ES 為了保證叢集的可用性,提供了 Replicas(副本)支援,然而每個副本也會執行分析、索引及可能的合併過程,所以 Replicas 的數量會嚴重影響寫索引的效率。
當寫索引時,需要把寫入的資料都同步到副本節點,副本節點越多,寫索引的效率就越慢。
如果我們需要大批量進行寫入操作,可以先禁止 Replica 複製,設定 index.number_of_replicas: 0 關閉副本。在寫入完成後,Replica 修改回正常的狀態。
- 合理使用合併
Lucene 以段的形式儲存資料。當有新的資料寫入索引時,Lucene 就會自動建立一個新的段。
隨著資料量的變化,段的數量會越來越多,消耗的多檔案控制代碼數及 CPU 就越多,查詢效率就會下降。
由於 Lucene 段合併的計算量龐大,會消耗大量的 I/O,所以 ES 預設採用較保守的策略,讓後臺定期進行段合併,如下所述:
索引寫入效率下降:當段合併的速度落後於索引寫入的速度時,ES 會把索引的執行緒數量減少到 1。
這樣可以避免出現堆積的段數量爆發,同時在日誌中打印出“now throttling indexing”INFO 級別的“警告”資訊。
提升段合併速度:ES 預設對段合併的速度是 20m/s,如果使用了 SSD,我們可以通過以下的命令將這個合併的速度增加到 100m/s。
PUT /_cluster/settings
{
"persistent" : {
"indices.store.throttle.max_bytes_per_sec" : "100mb"
}
}
讀取速度優化
不需要評分時,使用過濾查詢,減少評分過程
文件模型
文件應該合理建模,特別是應該避免join操作,巢狀(nested)會使查詢慢幾倍,父子關係可能使查詢慢數百倍。優化日期搜尋
在使用日期範圍檢索時,使用now的查詢通常不能快取,因為匹配到的範圍一直在變化,我們可以將日期四捨五入到分鐘,甚至可以將時間分為一個大的可快取部分和一個小的不可快取部分。避免大結果集和深翻
定期刪除
由於在 Lucene 中段具有不變性,每次進行刪除操作後不會立即從硬碟中進行實際的刪除,而是產生一個 .del 檔案記錄刪除動作。
隨著刪除操作的增長,.del 檔案會越來也多。當我們進行查詢操作的時候,被刪除的資料還會參與檢索中,然後根據 .del 檔案進行過濾。.del 檔案越多,查詢過濾過程越長,進而影響查詢的效率。
當機器空閒時,我們可以通過如下命令刪除檔案,來提升查詢的效率
https://cloud.tencent.com/developer/article/1436787
https://cloud.tencent.com/developer/article/1436787
https://www.jianshu.com/p/472963f7a3f4
https://www.jianshu.com/p/883325b7bbda?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
https://zhuanlan.zhihu.com/p/43437056