A. es是什麼
Elasticsearch是一個基於Lucene的搜索伺服器。它提供了一個分布式多用戶能力的全文搜索引擎,基於RESTful web介面。Elasticsearch是用Java語言開發的,並作為Apache許可條款下的開放源碼發布,是一種流行的企業級搜索引擎。
Elasticsearch用於雲計算中,能夠達到實時搜索,穩定,可靠,快速,安裝使用方便。官方客戶端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和許多其他語言中都是可用的。根據DB-Engines的排名顯示,Elasticsearch是最受歡迎的企業搜索引擎,其次是Apache Solr,也是基於Lucene。
相關信息:
Elasticsearch可以用於搜索各種文檔。它提供可擴展的搜索,具有接近實時的搜索,並支持多租戶。Elasticsearch是分布式的,這意味著索引可以被分成分片,每個分片可以有0個或多個副本。每個節點託管一個或多個分片,並充當協調器將操作委託給正確的分片。
再平衡和路由是自動完成的。相關數據通常存儲在同一個索引中,該索引由一個或多個主分片和零個或多個復制分片組成。一旦創建了索引,就不能更改主分片的數量。
B. ES是什麼
指音的指碼母顏文色字,s英色代型指彩色與網字指寫e色號指還號的母.字種拼也品產一絡.也.也.密情發
C. 匯編語言bp寄存器和es寄存器有何用
實際上,BP可以作SP使用,ES可以作DS使用。除了BP可以作為間接定址寄存器而SP不能以外,其餘的功能基本相同。Intel做這樣的設計也是為了避免因程序過大而寄存器不夠使用或者程序邏輯混亂的情況。
因為BP默認引用的段寄存器為SS,故當程序中設有多個棧,在不與SP沖突的情況下可以選用BP寄存器。一般在(相對)基址加變址定址方式中,以SS作為默認段地址,否則需要顯式指定段寄存器,這也可以說明,BP作存儲器指針的運用也比較多;
ES和DS的功能相同,同樣的道理,程序中設有多個數據段時,可以選用ES寄存器。一般在串處理時用得比較多。比如將一段內存空間存儲的數據復制到另一段空間,可以分別設置DS:SI指向源存儲數據的地址,ES:DI指向目的存儲數據的地址。
D. es使用與原理6 -- 聚合分析剖析
有些聚合分析的演算法,是很容易就可以並行的,比如說max
有些聚合分析的演算法,是不好並行的,比如說,count(distinct),並不是說,在每個node上,直接就出一些distinct value,就可以的,因為數據可能會很多,假設圖中的協調節點3百萬個數據去重後還剩下100萬distinct的數據,那麼內存需要來存儲這100萬條數據,這是不可能的
es會採取近似聚合的方式,就是採用在每個node上進行近估計的方式,得到最終的結論,cuont(distcint),100萬,1050萬/95萬 --> 5%左右的錯誤率
近似估計後的結果,不完全准確,但是速度會很快,一般會達到完全精準的演算法的性能的數十倍
precision_threshold優化准確率和內存開銷
brand去重,如果brand的unique value,在100個以內,小米,長虹,三星,TCL,HTL。。。
在多少個unique value以內,cardinality,幾乎保證100%准確
cardinality演算法,會佔用precision_threshold * 8 byte 內存消耗,100 * 8 = 800個位元組
佔用內存很小。。。而且unique value如果的確在值以內,那麼可以確保100%准確
100,數百萬的unique value,錯誤率在5%以內
precision_threshold,值設置的越大,佔用內存越大,1000 * 8 = 8000 / 1000 = 8KB,可以確保更多unique value的場景下,100%的准確
field,去重,count,這時候,unique value,10000,precision_threshold=10000,10000 * 8 = 80000個byte,80KB
doc value正排索引
搜索+聚合 是怎麼實現的?
假設是倒排索引實現的
倒排索引來實現是非常不現實的,因為我們搜索的那個欄位search_field 有可能是分詞的,這就需要去掃描整個索引才能實現聚合操作,效率是及其低下的。
正排索引結構:
doc2: agg1
doc3: agg2
1萬個doc --> 搜 -> 可能跟搜索到10000次,就搜索完了,就找到了1萬個doc的聚合field的所有值了,然後就可以執行分組聚合操作了
doc value原理
1、doc value原理
(1)index-time生成
PUT/POST的時候,就會生成doc value數據,也就是正排索引
(2)核心原理與倒排索引類似
正排索引,也會寫入磁碟文件中,然後呢,os cache先進行緩存,以提升訪問doc value正排索引的性能
如果os cache內存大小不足夠放得下整個正排索引,doc value,就會將doc value的數據寫入磁碟文件中
(3)性能問題:給jvm更少內存,64g伺服器,給jvm最多16g
es官方是建議,es大量是基於os cache來進行緩存和提升性能的,不建議用jvm內存來進行緩存,那樣會導致一定的gc開銷和oom問題
給jvm更少的內存,給os cache更大的內存
64g伺服器,給jvm最多16g,幾十個g的內存給os cache
os cache可以提升doc value和倒排索引的緩存和查詢效率
2、column壓縮
doc1: 550
doc2: 550
doc3: 500
合並相同值,550,doc1和doc2都保留一個550的標識即可
(1)所有值相同,直接保留單值
(2)少於256個值,使用table encoding模式:一種壓縮方式
(3)大於256個值,看有沒有最大公約數,有就除以最大公約數,然後保留這個最大公約數
重點:
對分詞的field,直接執行聚合操作,會報錯,大概意思是說,你必須要打開fielddata,然後將正排索引數據載入到內存中,才可以對分詞的field執行聚合操作,而且會消耗很大的內存
先修改 欄位的fielddata屬性為true,再查 就能查找到數據
當然,我們也可以使用內置field(keyword)不分詞,對string field進行聚合,如果對不分詞的field執行聚合操作,直接就可以執行,不需要設置fieldata=true
分詞field+fielddata的工作原理
doc value --> 不分詞的所有field,可以執行聚合操作 --> 如果你的某個field不分詞,那麼在index-time,就會自動生成doc value --> 針對這些不分詞的field執行聚合操作的時候,自動就會用doc value來執行
分詞field,是沒有doc value的。。。在index-time,如果某個field是分詞的,那麼是不會給它建立doc value正排索引的,因為分詞後,佔用的空間過於大,所以默認是不支持分詞field進行聚合的
分詞field默認沒有doc value,所以直接對分詞field執行聚合操作,是會報錯的
對於分詞field,必須打開和使用fielddata,完全存在於純內存中。。。結構和doc value類似。。。如果是ngram或者是大量term,那麼必將佔用大量的內存。。。
如果一定要對分詞的field執行聚合,那麼必須將fielddata=true,然後es就會在執行聚合操作的時候,現場將field對應的數據,建立一份fielddata正排索引,fielddata正排索引的結構跟doc value是類似的,
但是只會講fielddata正排索引載入到內存中來,然後基於內存中的fielddata正排索引執行分詞field的聚合操作
如果直接對分詞field執行聚合,報錯,才會讓我們開啟fielddata=true,告訴我們,會將fielddata uninverted index,正排索引,載入到內存,會耗費內存空間
為什麼fielddata必須在內存?因為大家自己思考一下,分詞的字元串,需要按照term進行聚合,需要執行更加復雜的演算法和操作,如果基於磁碟和os cache,那麼性能會很差
我們是不是可以預先生成載入fielddata到內存中來???
query-time的fielddata生成和載入到內存,變為index-time,建立倒排索引的時候,會同步生成fielddata並且載入到內存中來,這樣的話,對分詞field的聚合性能當然會大幅度增強
E. ES集群管理
上一篇文章:ES的索引管理 https://www.jianshu.com/p/3cb80dcf514f
ES通常以集群方式工作,這樣做不僅能夠提高 ES的搜索能力還可以處理大數據搜索的能力,同時也增加了系統的容錯能力及高可用,ES可以實現PB級數據的搜索。
下圖是ES集群結構的示意圖:
從上圖總結以下概念:
1、結點
ES集群由多個伺服器組成,每個伺服器即為一個Node結點(該服務只部署了一個ES進程)。
2、分片
當我們的文檔量很大時,由於內存和硬碟的限制,同時也為了提高ES的處理能力、容錯能力及高可用能力,我們將索引分成若干分片,每個分片可以放在不同的伺服器,這樣就實現了多個伺服器共同對外提供索引及搜索服務。
一個搜索請求過來,會分別從各各分片去查詢,最後將查詢到的數據合並返回給用戶。
3、副本
為了提高ES的高可用同時也為了提高搜索的吞吐量,我們將分片復制一份或多份存儲在其它的伺服器,這樣即使當前的伺服器掛掉了,擁有副本的伺服器照常可以提供服務。
4、主結點
一個集群中會有一個或多個主結點,主結點的作用是集群管理,比如增加節點,移除節點等,主結點掛掉後ES會重新選一個主結點。
5、結點轉發
每個結點都知道其它結點的信息,我們可以對任意一個結點發起請求,接收請求的結點會轉發給其它結點查詢數據。
下邊的例子實現創建一個2結點的集群,並且索引的分片我們設置2片,每片一個副本
主結點:master節點主要用於集群的管理及索引 比如新增結點、分片分配、索引的新增和刪除等。 數據結點:data 節點上保存了數據分片,它負責索引和搜索操作。 客戶端結點:client 節點僅作為請求客戶端存在,client的作用也作為負載均衡器,client 節點不存數據,只是將請求均衡轉發到其它結點。
通過下邊兩項參數來配置結點的功能:
node.master: #是否允許為主結點
node.data: #允許存儲數據作為數據結點
node.ingest: #是否允許成為協調節點,
四種組合方式:
master=true,data=true:即是主結點又是數據結點
master=false,data=true:僅是數據結點
master=true,data=false:僅是主結點,不存儲數據
master=false,data=false:即不是主結點也不是數據結點,此時可設置ingest為true表示它是一個客戶端
解壓elasticsearch-6.2.1.zip
結點1對外服務的http埠是:9200
集群管理埠是9300
配置elasticsearch.yml
結點名:xc_node_1
elasticsearch.yml內容如下
操作與節點1一樣
1)使用head連上其中一個結點
上圖表示兩個結點已經創建成功。
2)下邊創建索引庫,共2個分片,每個分片一個副本
上圖可以看到共有4個分片,其中兩個分片是副本。
3)每個結點安裝IK分詞器
通過訪問 GET /_cluster/health 來查看Elasticsearch 的集群健康情況。
用三種顏色來展示健康狀態: green 、 yellow 或者 red 。
green:所有的主分片和副本分片都正常運行。
yellow:所有的主分片都正常運行,但有些副本分片運行不正常。
red:存在主分片運行不正常。
1)創建映射並寫入文檔
連接 其中任意一台結點,創建映射寫入文檔。
2)搜索
向其它一個結點發起搜索請求,查詢全部數據。
3)關閉一個結點
ES會重新選中一個主結點(前提在配置結點時允許它可以為主結點)
此時向活的結點發起搜索請求,仍然正常。
F. ES數據存儲可靠性和寫入流程
https://www.elastic.co/guide/en/elasticsearch/guide/2.x/near-real-time.html
https://www.elastic.co/guide/en/elasticsearch/guide/2.x/merge-process.html
1、數據存儲可靠性保證原理
1.1 translog機制
當一個文檔寫入Lucence後是存儲在內存中的,即使執行了refresh操作仍然是在文件系統緩存中,如果此時伺服器宕機,那麼這部分數據將會丟失
當進行文檔寫操作時會先將文檔寫入Lucene,然後寫入一份到translog,寫入translog是落盤的
tips:如果對可靠性要求不是很高,也可以設置非同步落盤,可以提高性能,由配置index.translog.rability和index.translog.sync_interval控制
tips:translog是追加寫入,因此性能比較好
先寫入Lucene再寫入translog。原因是寫入Lucene可能會失敗,為了減少寫入失敗回滾的復雜度,因此先寫入Lucene
1.2 flush操作
refresh_interval定時觸發 或當translog達到index.translog.flush_threshold_size(默認512mb),ES會觸發一次flush操作:先執行refresh操作將buffer中的數據生成segment,然後調用lucene的commit方法將所有內存中的segment fsync到磁碟,最後會清空translog中的數據(6.x版本為了實現sequenceIDs,不刪除translog) 。
1.3 merge操作
refresh操作會產生大量的小segment,因此產生的每個文件都會消耗文件句柄,內存,CPU 使用等各種資源。更重要的是每個查詢請求都要順序檢查每個segment; segment越多檢索會越慢.
ES會運行一個檢測任務,在後台把近似大小的segment合並成一個新的大segment,並刪除舊segment
1.4、多副本機制
ES有多副本機制(默認是1個副本),一個分片的主副分片不能分片在同一個節點上,進一步保證數據的可靠性。
2、ES寫索引的流程
G. es寫入數據的工作原理是什麼
1)客戶端任意選擇一個node發送請求過去,這個node就是coordinating node(協調節點)
2)coordinating node,對該數據經過hash後,判斷該數據屬於哪個shard進程,找到有該shard的primary shard的node,然後對document進行路由,將請求轉發給對應的node(有primary shard的結點)
3)具體接收的primary shard處理請求,然後將數據同步到replica node
4)coordinating node,如果發現primary node和所有replica node都搞定之後,就返回響應結果給客戶端
寫數據底層原理
1)先寫入buffer, 在buffer里的時候數據是搜索不到 的;同時將數據寫入 translog日誌文件 (防止宕機buffer數據丟失)
2)如果 buffer快滿了 , 或者到一定時間 ,就會將buffer數據refresh到一個 新的segment file中 ,但是此時數據不是直接進入segment file的磁碟文件的,而是先進入os cache的。這個過程就是refresh。
默認每隔1秒鍾,es將buffer中的數據寫入一個 新的segment file,每秒鍾會產生一個新的磁碟文件 segment file ,這個segment file中就存儲最近1秒內buffer中寫入的數據
但是如果buffer裡面此時 沒有數據 ,那當然 不會執行refresh操作,不會創建文件 ,如果buffer裡面有數據,默認1秒鍾執行一次refresh操作,刷入一個新的segment file中;
操作系統裡面,磁碟文件其實都有一個東西,叫做os cache,操作系統緩存,就是說 數據寫入磁碟文件之前,會先進入os cache,先進入操作系統級別的一個內存緩存中去,再進入磁碟
只要buffer中的數據被refresh操作,刷入os cache中,就代表這個數據就可以被搜索到了 , 只要數據被輸入os cache中,buffer就會被清空了,因為不需要保留buffer了,數據在translog裡面已經持久化到磁碟去一份了
3)只要數據進入os cache,此時就可以讓這個segment file的數據對外提供搜索了
4)重復1~3步驟,新的數據不斷進入buffer和translog,不斷將buffer數據寫入一個又一個新的segment file中去(數據寫入到segment file里後就建立好了倒排索引),每次refresh完buffer清空,translog保留。隨著這個過程推進,translog會變得越來越大。 當translog達到一定長度的時候,就會觸發 translog 的commit操作。
buffer中的數據,倒是好,每隔1秒就被刷到os cache中去,然後這個buffer就被清空了。所以說這個buffer的數據始終是可以保持住不會填滿es進程的內存的。
每次一條數據寫入buffer,同時會寫入一條日誌到translog日誌文件中去,所以這個translog日誌文件是不斷變大的,當translog日誌文件大到一定程度的時候,就會執行commit操作。
5) commit操作 發生第一步,就是 將buffer中現有數據refresh到os cache中去,清空buffer
6)將一個 commit point寫入 磁碟文件 , 裡面標識著這個commit point對應的所有segment file
7)強行將os cache中目前所有的數據都fsync到磁碟文件中去
commit操作:1、寫commit point;2、將os cache數據fsync強刷到磁碟上去;3、清空translog日誌文件
8) 將現有的translog清空,然後再次重啟啟用一個translog,此時commit操作完成。
默認每隔30分鍾 會自動執行一次commit,但是如果 translog過大,也會觸發commit。
整個 commit的過程,叫做flush操作 。我們可以手動執行flush操作,就是 將所有os cache數據刷到磁碟文件中去。
不叫做commit操作,flush操作。 es中的flush操作,就對應著tanslog commit的全過程 。我們也可以通過es api,手動執行flush操作,手動將os cache中的數據fsync強刷到磁碟上去,記錄一個commit point,清空translog日誌文件。
9) translog其實也是先寫入os cache的,默認每隔5秒刷一次到磁碟中去
所以默認情況下, 可能有5秒的數據會僅僅停留在buffer或者translog文件的os cache中,如果此時機器掛了,會丟失5秒鍾的數據。但是這樣性能比較好,最多丟5秒的數據。也可以將translog設置成每次寫操作必須是直接fsync到磁碟,但是性能會差很多。
10)如果是刪除操作,commit的時候會生成一個.del文件(磁碟),裡面將某個doc標識為deleted狀態,那麼搜索的時候根據.del文件就知道這個doc被刪除了
11)如果是 更新操作,就是將原來的doc標識為deleted狀態,然後新寫入一條數據
12)buffer每次refresh一次,就會產生一個segment file,所以默認情況下是1秒鍾一個segment file, segment file會越來越多,此時會定期執行merge
13) 每次merge的時候,會將多個segment file合並成一個,同時這里會將標識為deleted的doc給物理刪除掉,然後將新的segment file寫入磁碟,這里會寫一個commit point,標識所有新的segment file,然後打開segment file供搜索使用,同時刪除舊的segment file。
H. PB級大規模Elasticsearch集群運維與調優實踐
某中型互聯網公司的游戲業務,使用了騰訊雲的Elasticsearch產品,採用ELK架構存儲業務日誌。因為游戲業務本身的日誌數據量非常大(寫入峰值在100w qps),在服務客戶的幾個月中,踩了不少坑,經過數次優化與調整,把客戶的ES集群調整的比較穩定,避免了在業務高峰時客戶集群的讀寫異常,並且降低了客戶的資金成本和使用成本。下面把服務客戶過程中遇到的典型問題進行梳理,總結經驗,避免再次踩坑。
解決方案架構師A: bellen, XX要上線一款新游戲,日誌存儲決定用ELK架構,他們決定在XX雲和我們之間二選一,我們首先去他們公司和他們交流一下,爭取拿下!
bellen: 好,隨時有空!
。。。
和架構師一起前往該公司,跟負責底層組件的運維部門的負責人進行溝通。
XX公司運維老大:不要講你們的PPT了,先告訴我你們能給我們帶來什麼!
bellen: 。。。呃,我們有很多優勢。。。比如靈活地擴容縮容集群,還可以一鍵平滑升級集群版本,並且提供有跨機房容災的集群從而實現高可用。。
XX公司運維老大:你說的這些別的廠商也有,我就問一個問題,我們現在要存儲一年的游戲日誌,不能刪除數據,每天就按10TB的數據量算,一年也得有個3PB多的數據,這么大的數量,都放在SSD雲盤上,我們的成本太高了,你們有什麼方案既能夠滿足我們存儲這么大數據量的需求,同時能夠降低我們的成本嗎?
bellen: 我們本身提供的有冷熱模式的集群,熱節點採用SSD雲硬碟,冷節點採用SATA盤,採用ES自帶的ILM索引生命周期管理功能定期把較老的索引從熱節點遷移到冷節點上,這樣從整體上可以降低成本。另外一方面,也可以定期把更老的索引通過snapshot快照備份到COS對象存儲中,然後刪除索引,這樣成本就更低了。
XX公司運維老大:存儲到COS就是冷存儲唄,我們需要查詢COS里的數據時,還得再把數據恢復到ES里?這樣不行,速度太慢了,業務等不了那麼長時間,我們的數據不能刪除,只能放在ES里!你們能不能給我們提供一個API, 讓老的索引數據雖然存儲在COS里,但是通過這個API依然可以查詢到數據,而不是先恢復到ES, 再進行查詢?
bellen: 。。。呃,這個可以做,但是需要時間。是否可以採用hadoop on COS的架構,把存量的老的索引數據通過工具導入到COS,通過hive去查詢,這樣成本會非常低,數據依然是隨時可查的。
XX公司運維老大:那不行,我們只想用成熟的ELK架構來做,再增加hadoop那一套東西,我們沒那麼多人力搞這個事!
bellen: 好吧,那可以先搞一個集群測試起來,看看性能怎麼樣。關於存量數據放在COS里但是也需要查詢的問題,我們可以先制定方案,盡快實施起來。
XX公司運維老大:行吧,我們現在按每天10TB數據量預估,先購買一個集群,能撐3個月的數據量就行,能給一個集群配置的建議嗎?
bellen: 目前支持單節點磁碟最大6TB, cpu和內存的話可以放到8核32G單節點,單節點跑2w qps寫入沒有問題,後面也可以進行縱向擴容和橫向擴容。
XX公司運維老大:好,我們先測試一下。
N 天後,架構師A直接在微信群里反饋:"bellen, 客戶反饋這邊的ES集群性能不行啊,使用logstash消費kafka中的日誌數據,跑了快一天了數據還沒追平,這是線上的集群,麻煩緊急看一下吧。。"
我一看,一臉懵, 什麼時候已經上線了啊,不是還在測試中嗎?
XX公司運維小B: 我們購買了8核32G*10節點的集群,單節點磁碟6TB, 索引設置的10分片1副本,現在使用logstash消費kafka中的數據,一直沒有追平,kafka中還有很多數據積壓,感覺是ES的寫入性能有問題。
隨後我立即查看了集群的監控數據,發現cpu和load都很高,jvm堆內存使用率平均都到了90%,節點jvm gc非常頻繁了,部分節點因為響應緩慢,不停的離線又上線。。
經過溝通,發現用戶的使用姿勢是filebeat+kafka+logstash+elasticsearch, 當前已經在kafka中存儲了有10天的日誌數據,啟動了20台logstash進行消費,logstash的batch size也調到了5000,性能瓶頸是在ES這一側。客戶8核32G*10節點的集群,理論上跑10w qps沒有問題,但是logstash消費積壓的數據往ES寫入的qps遠不止10w,所以是ES扛不住寫入壓力了,所以只能對ES集群進行擴容,為了加快存量數據的消費速度,先縱向擴容單節點的配置到32核64GB,之後再橫向增加節點,以保證ES集群能夠最大支持100w qps的寫入(這里需要注意的是,增加節點後索引的分片數量也需要調整)。
所以一般新客戶接入使用ES時,必須要事先評估好節點配置和集群規模,可以從以下幾個方面進行評估:
上述場景2遇到的問題是業務上線前沒有對集群配置和規模進行合理的評估,導致上線後ES集群負載就很高,通過合理的擴容處理,集群最終抗住了寫入壓力。但是又有新的問題出現了。
因為kafka積壓的數據比較多,客戶使用logstash消費kafka數據時,反饋有兩個問題:
經過分析客戶logstash的配置文件,發現問題出現的原因主要是:
分析後,對kafka和logstash進行了如下優化:
通過上述優化,最終使得logstash機器資源都被充分利用上,很快消費完堆積的kafka數據,待消費速度追平生成速度後,logstash消費kafka一直穩定運行,沒有出現積壓。
另外,客戶一開始使用的是5.6.4版本的logstash,版本較老,使用過程中出現因為單個消息體過長導致logstash拋異常後直接退出的問題:
通過把logstash升級至高版本6.8避免了這個問題(6.x版本的logstash修復了這個問題,避免了crash)。
客戶的游戲上線有一個月了,原先預估每天最多有10TB的數據量,實際則是在運營活動期間每天產生20TB的數據,原先6TB*60=360TB總量的數據盤使用率也達到了80%。針對這種情況,我們建議客戶使用冷熱分離的集群架構,在原先60個熱節點的基礎上,增加一批warm節點存儲冷數據,利用ILM(索引生命周期管理)功能定期遷移熱節點上的索引到warm節點上。
通過增加warm節點的方式,客戶的集群磁碟總量達到了780TB, 可以滿足最多三個月的存儲需求。但是客戶的需求還沒有滿足:
XX公司運維老大:給我們一個能存放一年數據的方案吧,總是通過加節點擴容磁碟的方式不是長久之計,我們得天天盯著這個集群,運維成本很高!並且一直加節點,ES會扛不住吧?
bellen: 可以嘗試使用我們新上線的支持本地盤的機型,熱節點最大支持7.2TB的本地SSD盤,warm節點最大支持48TB的本地SATA盤。一方面熱節點的性能相比雲盤提高了,另外warm節點可以支持更大的磁碟容量。單節點可以支持的磁碟容量增大了,節點數量就不用太多了,可以避免踩到因為節點數量太多而觸發的坑。
XX公司運維老大:現在用的是雲盤,能替換成本地盤嗎,怎麼替換?
bellen: 不能直接替換,需要在集群中新加入帶本地盤的節點,把數據從老的雲盤節點遷移到新的節點上,遷移完成後再剔除掉舊的節點,這樣可以保證服務不會中斷,讀寫都可以正常進行。
XX公司運維老大:好,可以實施,盡快搞起來!
雲盤切換為本地盤,是通過調用雲服務後台的API自動實施的。在實施之後,觸發了數據從舊節點遷移到新節點的流程,但是大約半個小時候,問題又出現了:
XX公司運維小B: bellen, 快看一下,ES的寫入快掉0了。
bellen: 。。。
通過查看集群監控,發現寫入qps直接由50w降到1w,寫入拒絕率猛增,通過查看集群日誌,發現是因為當前小時的索引沒有創建成功導致寫入失敗。
緊急情況下,執行了以下操作定位到了原因:
經過了這次擴容操作,總結了如下經驗:
在穩定運行了一陣後,集群又出問題了。。
XX公司運維小B: bellen, 昨晚凌晨1點鍾之後,集群就沒有寫入了,現在kafka里有大量的數據堆積,麻煩盡快看一下?
bellen: 。。。
通過cerebro查看集群,發現集群處於yellow狀態,然後發現集群有大量的錯誤日誌:
然後再進一步查看集群日誌,發現有"master not discovered yet..."之類的錯誤日誌,檢查三個master節點,發現有兩個master掛掉,只剩一個了,集群無法選主。
登陸到掛了了master節點機器上,發現保活程序無法啟動es進程,第一直覺是es進程oom了;此時也發現master節點磁碟使用率100%, 檢查了JVM堆內存快照文件目錄,發現有大量的快照文件,於是刪除了一部分文件,重啟es進程,進程正常啟動了;但是問題是堆內存使用率太高,gc非常頻繁,master節點響應非常慢,大量的創建索引的任務都超時,阻塞在任務隊列中,集群還是無法恢復正常。
看到集群master節點的配置是16核32GB內存,JVM實際只分配了16GB內存,此時只好通過對master節點原地增加內存到64GB(虛擬機,使用的騰訊雲CVM, 可以調整機器規格,需要重啟),master節點機器重啟之後,修改了es目錄jvm.options文件,調整了堆內存大小,重新啟動了es進程。
3個master節點都恢復正常了,但是分片還需要進行恢復,通過GET _cluster/health看到集群當前有超過10w個分片,而這些分片恢復還需要一段時間,通過調大"cluster.routing.allocation.node_concurrent_recoveries", 增大分片恢復的並發數量。實際上5w個主分片恢復的是比較快的了,但是副本分片的恢復就相對慢很多,因為部分副本分片需要從主分片上同步數據才能恢復。此時可以採取的方式是把部分舊的索引副本數量調為0, 讓大量副本分片恢復的任務盡快結束,保證新索引能夠正常創建,從而使得集群能夠正常寫入。
總結這次故障的根本原因是集群的索引和分片數量太多,集群元數據佔用了大量的堆內存,而master節點本身的JVM內存只有16GB(數據節點有32GB), master節點頻繁full gc導致master節點異常,從而最終導致整個集群異常。所以要解決這個問題,還是得從根本上解決集群的分片數量過多的問題。
目前日誌索引是按照小時創建,60分片1副本,每天有24*60*2=2880個分片,每個月就產生86400個分片,這么多的分片可能會帶來嚴重的問題。有以下幾種方式解決分片數量過多的問題:
和客戶溝通過後,客戶表示可以接受方式1和方式2,但是方式3和4不能接受,因為考慮到存在磁碟故障的可能性,必須保留一個副本來保證數據的可靠性;另外還必須保證所有數據都是隨時可查詢的,不能關閉。
在場景5中,雖然通過臨時給master節點增加內存,抗住了10w分片,但是不能從根本上解決問題。客戶的數據是計劃保留一年的,如果不進行優化,集群必然扛不住數十萬個分片。所以接下來需要著重解決集群整體分片數量過多的問題,在場景5的最後提到了,用戶可以接受開啟shrink以及降低索引創建粒度(經過調整後,每兩個小時創建一個索引),這在一定程度上減少了分片的數量,能夠使集群暫時穩定一陣。
輔助客戶在kibana上配置了如下的ILM策略:
在warm phase, 把創建時間超過360小時的索引從hot節點遷移到warm節點上,保持索引的副本數量為1,之所以使用360小時作為條件,而不是15天作為條件,是因為客戶的索引是按小時創建的,如果以15天作為遷移條件,則在每天凌晨都會同時觸發15天前的24個索引一共24*120=2880個分片同時開始遷移索引,容易引發場景4中介紹的由於遷移分片數量過多導致創建索引被阻塞的問題,所以以360小時作為條件,則在每個小時只會執行一個索引的遷移,這樣把24個索引的遷移任務打平,避免其它任務被阻塞的情況發生。
同時,也在warm phase階段,設置索引shrink,把索引的分片數縮成5個,因為老的索引已經不執行寫入了,所以也可以執行force merge, 強制把segment文件合並為1個,可以獲得更好的查詢性能。
另外,設置了ILM策略後,可以在索引模板里增加index.lifecycle.name配置,使得所有新創建的索引都可以和新添加的ILM策略關聯,從而使得ILM能夠正常運行。
客戶使用的ES版本是6.8.2, 在運行ILM的過程中, 也發現一些問題:
這是因為shrink操作需要新把索引完整的一份數據都遷移到一個節點上,然後在內存中構建新的分片元數據,把新的分片通過軟鏈接指向到幾個老的分片的數據,在ILM中執行shrink時,ILM會對索引進行如下配置:
問題是索引包含副本,而主分片和副本分片又不能在同一個節點上,所以會出現部分分片無法分配的情況(不是全部,只有一部分),這里應該是觸發了6.8版本的ILM的bug,需要查看源碼才能定位解決這個bug,目前還在研究中。當前的workaround是通過腳本定期掃描出現unassigned shards的索引,修改其settings:
優先保證分片先從hot節點遷移到warm節點,這樣後續的shrink才能順利執行(也可能執行失敗,因為60個分片都在一個節點上,可能會觸發rebalance, 導致分片遷移走,shrink的前置條件又不滿足,導致執行失敗)。要完全規避這個問題,還得在ILM策略中設置,滿足創建時間超過360個小時的索引,副本直接調整為0,但是客戶又不接受,沒辦法。
在場景5和6中,介紹了10w個分片會給集群帶來的影響和通過開啟shrink來降低分片數量,但是仍然有兩個需要重點解決的問題:
可以估算一下,按小時建索引,60分片1副本,一年的分片數為24*120*365=1051200個分片,執行shrink後分片數量24*10*350 + 24*120*15 = 127200(15天內的新索引為了保障寫入性能和數據可靠性,仍然保持60分片1副本,舊的索引shrink為5分片1副本), 仍然有超過10w個分片。結合集群一年總的存儲量和單個分片可以支持的數據量大小進行評估,我們期望集群總體的分片數量可以穩定為6w~8w,怎麼優化?
可以想到的方案是執行數據冷備份,把比較老的索引都冷備到其它的存儲介質上比如HDFS,S3,騰訊雲的COS對象存儲等,但是問題是這些冷備的數據如果也要查詢,需要先恢復到ES中才可查,恢復速度比較慢,客戶無法接受。由此也產生了新的想法,目前老的索引仍然是1副本,可以把老索引先進行冷備份,再把副本調為0,這樣做有以下幾點好處:
經過和客戶溝通,客戶接受了上述方案,計劃把老索引冷備到騰訊雲的對象存儲COS中,實施步驟為:
其中步驟1的實施可以通過腳本實現,本案例中採用騰訊雲SCF雲函數進行實施,方便快捷可監控。實施要點有:
在實施完步驟1之後,就可以批量把對索引進行過備份的索引副本數都調為0, 這樣一次性釋放了很多磁碟空間,並且顯著降低了集群整體的分片數量。
接下來實施步驟2,需要每天執行一次快照,多創建時間較久的索引進行備份,實施比較簡單,可以通過crontab定時執行腳本或者使用騰訊雲SCF執行。
步驟2實施之後,就可以修改ILM策略,開啟cold phase, 修改索引副本數量為0:
此處的timing是創建時間20天後,需要保證步驟2中對過去老索引數據備份先執行完成才可以進入到cold phase.
通過老索引數據冷備並且降低索引副本,我們可以把集群整體的分片數量維持在一個較低的水位,但是還有另外一個問題待解決,也即shrink失敗的問題。剛好,我們可以利用對老索引數據冷備並且降低索引副本的方案,來徹底解決shrink失敗的問題。
在場景5中有提到,shrink失敗歸根接地是因為索引的副本數量為1, 現在我們可以吧數據備份和降低副本提前,讓老索引進入到ILM的warm phase中時已經是0副本,之後再執行shrink操作就不會有問題了;同時,因為副本降低了,索引從hot節點遷移到warm節點遷移的數據量也減少了一半,從而降低了集群負載,一舉兩得。
因此,我們需要修改ILM策略,在warm phase就把索引的副本數量調整為0, 然後去除cold phase。
另外一個可選的優化項是,對老的索引進行凍結,凍結索引是指把索引常駐內存的一些數據從內存中清理掉(比如FST, 元數據等), 從而降低內存使用量,而在查詢已經凍結的索引時,會重新構建出臨時的索引數據結構存放在內存中,查詢完畢再清理掉;需要注意的是,默認情況下是無法查詢已經凍結的索引的,需要在查詢時顯式的增加"ignore_throttled=false"參數。
經過上述優化,我們最終解決了集群整體分片數量過多和shrink失敗的問題。在實施過程中引入了額外的定時任務腳本實施自動化快照,實際上在7.4版本的ES中,已經有這個功能了,特性名稱為 SLM (快照生命周期管理),並且可以結合ILM使用,在ILM中增加了"wait_for_snapshot"的ACTION, 但是卻只能在delete phase中使用,不滿足我們的場景。
在上述的場景4-7中,我們花費大量的精力去解決問題和優化使用方式,保證ES集群能夠穩定運行,支持PB級別的存儲。溯本回原,如果我們能有一個方案使得客戶只需要把熱數據放在SSD盤上,然後冷數據存儲到COS/S3上,但同時又使冷數據能夠支持按需隨時可查,那我們前面碰到的所有問題都迎刃而解了。可以想像得到的好處有:
而這正是目前es開源社區正在開發中的Searchable Snapshots功能,從 Searchable Snapshots API 的官方文檔上可以看到,我們可以創建一個索引,將其掛載到一個指定的快照中,這個新的索引是可查詢的,雖然查詢時間可能會慢點,但是在日誌場景中,對一些較老的索引進行查詢時,延遲大點一般都是可以接受的。
所以我認為,Searchable Snapshots解決了很多痛點,將會給ES帶了新的繁榮!
經歷過上述運維和優化ES集群的實踐,我們總結到的經驗有:
從一開始和客戶進行接觸,了解客戶訴求,逐步解決ES集群的問題,最終使得ES集群能夠保持穩定,這中間的經歷讓我真真正正的領悟到"實踐出真知",只有不斷實踐,才能對異常情況迅速做出反應,以及對客戶提的優化需求迅速反饋。
I. 怎麼用spring獲取es數據
1. ES和solr都是作為全文搜索引擎出現的。都是基於Lucene的搜索伺服器。
2. ES不是可靠的存儲系統,不是資料庫,它有丟數據的風險。
3. ES不是實時系統,數據寫入成功只是trans log成功(類似於MySQL的bin log),寫入成功後立刻查詢查不到是正常的。因為數據此刻可能還在內存里而不是進入存儲引擎里。同理,刪除一條數據後也不是馬上消失。寫入何時可查詢?ES內部有一個後台線程,定時將內存中的一批數據寫入到存儲引擎,此後數據可見。默認後台線程一秒運行一次。該線程運行的越頻繁,寫入性能越低。運行的頻率越低,寫入的性能越高(不會無限高)。
4. 目前已知的單ES集群可以存儲PB級別的數據,不過這個就非常費勁了。TB級別數據沒壓力。
5. 如果使用ES官方提供的jar包訪問,需要JDK1.7及以上。
6. 使用對應的版本訪問ES server。如果ES server端的版本是1.7,那麼請使用ES 1.7的client。如果ES server是2.1,請使用2.1的client。
7. ES索引存在Linux伺服器的文件系統之上(背後是文件系統,不是類似於HDFS的分布式文件系統)
8. ES Java client是線程安全的,全局構建一個即可滿足讀寫需求,不要每次都創建ES client。每次訪問ES都構建新的es client即會拋出次異常。
9. 非常不建議使用ES的動態識別和創建的機制,因為很多情況下這並非你所需要。推薦的做法是在寫數據之前仔細的創建mapping。
10. 強烈不建議在ES中使用深分頁。可能會導致集群不可用。
11. ES是靜態分片,一旦分片數在創建索引時確定那麼後繼不能修改。
12. ES里提供了type,很多人以為type是物理表,一個type的數據是獨立存儲的;但是在ES內部並不是這樣,type在ES內部僅僅是一個欄位。所以在很多數據能分為獨立index的情況下,不要放到一個index里用type去分。只有嵌套類和父子類的情況下使用type才是合理的。
13. ES並不提供原生的中文分詞的能力。有第三方的中文分詞的插件,比如ik等。Ik是個toy分詞器,有嚴肅的分詞需求的話,請在使用ES之前使用獨立的分詞器分好詞後向ES寫入。
14. ES中的index,首先會進行分片,每一個分片數據一般都會有自己的副本數據,ES分配分片的策略會保證同一個分片數據和自己的副本不會分配到同一個節點上。當集群中的某一節點宕機後,ES的master在ping該節點時通過一定的策略會發現該節點不存活;會開啟ES的恢復過程
15. ES沒有update的能力。所有的update都是標記刪除老文檔,然後重新insert一條新文檔。
J. Elasticsearch 能夠存儲的數據量一般有多大
單獨看ES能玩多大數據意義不大,具體實踐中往往因為各種業務要求而無法繼續增加數據量。目大的方面考慮有如下幾點:
1、查詢速度。ES可以支持的查詢類型多種多樣,單一的term匹配,復雜的historm agg,甚至父子文檔模式下bool查詢之後繼續做文本高亮,數據量越大查詢時間越長。如果只是簡單的把數據寫進去然後按照ID獲取數據,那就盡管往裡面寫數據吧。
2、寫入速度。數據量越大,寫入速度受影響的可能性越大。業務要求1小時的數據1小時內必須寫完,如果做不到就得考慮分索引或者分集群了。
3、更新速度。同上,更新比單純的寫入操作更多,先get再merge再overwrite到es。
4、其他因素。
目前我遇到的ES集群,有1.5T-2T索引量的情況下,需要支持平均查詢在500ms以內的高並發高亮查詢。在我們的場景下這個量級不算小了。