版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接和本聲明。
本文連接:https://blog.csdn.net/Norman_Hu/article/details/89917569
1. 背景
因爲項目初期設計問題,採集到es的業務日誌只使用了一個索引(index),隨着線上日誌量的增加,es很快飆升到磁盤警惕線,網上找了一圈,不少文章版本都比較老舊,最終直接啃官方文檔,沒有copy別人博客,若是你中途不走神,本文總共15min。version 適用於es 6.0+node
本文前提保障:算法
文檔中有時間字段,方便按日期切割
index的mapping配置中,_source需爲 true(默認),以保證es存入了源文檔,而不只僅是docId,便於執行reindex!
curl -XGET "localhost:9200/your_index_name/_mapping"
# 若是沒有顯示 _source, 表明"_source": false
1
2
2. 刪文檔(不建議)
這第一個想到的方法,將最老舊的日誌刪掉,如只保留近3個月的,採用 delete_by_query 接口json
curl -X POST "localhost:9200/twitter/_delete_by_query" -H 'Content-Type: application/json' -d'
{
"query": {
"range" : {
"day" : {
"lt" : "2018-12-01"
}
}
}
}
'
1
2
3
4
5
6
7
8
9
10
11
可是,es的delete,並非真正的物理刪,磁盤使用率(utilization)並不會降低。刪除的文檔僅僅被標記,es將文檔存入一個個segment file中,其file數量隨着文檔的寫入不斷增長,es所以會有合併segment file的操做,將多個小的segment合併成一個大的segment file,當segment合併的時候,標記的文檔纔會真正的物理刪除。api
這種方案,只適合在 項目早期、文檔量少、且機器負載不高 狀況下進行,由於批量讀寫會致使cpu utilization的飆升,形成系統負載加劇,能夠在晚上業務量不高的時候進行多線程
3. 磁盤擴容 (短時間有效)
那若是標記刪除不能當即解決問題,那就對磁盤擴容吧!1T 到 2T,2T 到 4T,因爲升級磁盤須要重啓機器,因此,如何作到優雅滾動關停es相當重要!app
3.1 中止es集羣服務
常見就是ps看一下es的進程,而後kill,先等一下!在此以前,需對集羣進行配置,方便更加快速的服務重啓curl
3.1.1 中止分片分配
因爲es中的index是分佈式存儲,因此一個index分紅了多個shard,分佈在各個節點node上,每一個shard均可以單獨提供服務,同時每一個shard能夠配置多個副本(replicas),保證集羣的高可用,提升了查詢效率。同時,es在管理這些分片時,有一套自有的均衡算法,保證shard均勻分散在各個node上,同時保證每一個shard和其對應的replica不在同一node上。正是這種機制,使得當node離開和從新加入的時候,分片的分配會copy文件,會形成大量的io,由於node重啓很快就回來,因此暫時關掉自動分片分配,詳情參見另外一篇文章,介紹集羣重啓步驟。這裏選擇直接中止分配分佈式
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
"persistent": {
"cluster.routing.allocation.enable": "none"
}
}'
1
2
3
4
5
6
這樣,在node停掉的時候,不會出現分片分配從新均衡了。ide
3.1.2 掛載新盤
因爲機器是雲上資源,所以按照各個雲上的擴容文檔操做便可,不管是自有機房仍是雲上機器,無非是下面幾個步驟:學習
reboot重啓
df -h 看一下如今的掛載點(如/dev/vdb)和 掛載路徑(如/data/es/)
fdisk -l 能夠進一步確認盤的信息
fdisk /dev/vdb,對新盤進行從新掛載,命令中可能涉及到的選項,不分區的話,大部分按回車默認選項:d n p 1 wq
其餘操做 如e2fsck -f /dev/vdb,resize2fs /dev/vdb
mount /dev/vdb /data 從新掛載便可
3.1.3 恢復分配分配
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
"transient": {
"cluster.routing.allocation.enable": "all"
}
}
'
1
2
3
4
5
6
7
這一步,會有部分io,等到recovery結束,集羣恢復正常。若是有 Unassigned 狀態的shard,須要手動執行分片分配,對應的命令reroute,這個不難,正常狀況下,幾乎不會出現 Unassigned 的shard。
執行完上述操做,磁盤體積翻倍。在短時間內,能夠保證服務正常。
4. 索引拆分(最佳實踐)
不管標記刪除,仍是磁盤擴容,都沒有真正的解決索引過大的問題,隨着文檔數的增長,索引勢必會變得更大。針對大索引,標記刪除以及擴容後分片恢復的操做時間,也會大幅增長,可能會從幾小時到一兩天。上述方法不可行!
由於es對index的刪除是物理刪除,是當即的,既然不能直接刪除原索引,得想辦法把大索引拆成小索引,而後再刪除老舊的。那麼es有麼有這種api,將大的索引按照某種條件(按天、按月)進行拆分呢?爲此,我找了不少博客,最後發現,不少博客裏介紹的api都很老舊,仍是官網的文檔最實在,文檔是英文,不過理解起來並不複雜。牆裂推薦看官方文檔。爲此,我調研了這些api:_rollover,alias,template,reindex,你們能夠有針對的深刻學習這幾個api,細節這裏就不講了,先看我怎麼用的吧~
4.1. 錯誤嘗試
_rollover 當看到這個api時,我覺得找到了最接近需求的api,這個api介紹以下
The rollover index API rolls an alias over to a new index when the existing index is considered to be too large or too old.
顯然被 too large 和 too old 吸引了。。。
curl -X PUT "localhost:9200/logs-000001" -H 'Content-Type: application/json' -d'
{
"aliases": {
"logs_write": {}
}
}
'
# Add > 1000 documents to logs-000001
curl -X POST "localhost:9200/logs_write/_rollover" -H 'Content-Type: application/json' -d'
{
"conditions": {
"max_age": "7d", #當文檔建立時間大於7天
"max_docs": 1000, #當文檔數量超過上限1000
"max_size": "5gb" #當索引大小超過5GB
}
}
'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
思路:
先給 index 建一個別名 alias,再 rollover,當執行時,知足任一個條件便可建立新索引,同時別名 alias 指向新的,除此以外,_rollover還能夠按日期生成新索引
問題:
拆分只在api執行時生效,後面不會自動拆分,若是按天索引,則必須天天定時執行,增長維護成本,若是定時任務失敗…
當別名滾向新的索引後,舊索引不能經過 alias 再訪問
並無將歷史的索引按天生成,只是在此api執行後,新文檔纔會進新index
4.2 最佳實踐
假設原索引name knight-log,當前月份 2019-02
思路:
建立別名alias:alias-knight-log
爲舊的 knight-log 關聯別名 alias-knight-log
建立索引模板template: template-knight-log,該模板匹配全部 knight-log.* 索引,在模板中定義settings和mappings,以及自動關聯別名 alias-knight-log
此外,將項目中讀寫es的操做分別設置:
讀 alias-knight-log,能夠查詢全部關聯索引
寫 knight-log.2019-02,在原索引後append月份便可!調用Java api進行索引時,接口規定每條文檔須要指定index name,所以將原來的 name 變爲 name + current_month 便可,其餘語言同理!
更新業務代碼,從新上線後,新文檔會進入到形如 knight-log.2019-02 的索引中,原 knight-log 將不會有寫請求,讀請求也通過別名 alias-knight-log 代理了
通過上述步驟,舊的index只讀不寫,寫請求所有進入 knight-log.2019-02 的索引中,保障了這一點,咱們就能夠對 knight-log 按時間粒度以大化小,逐個刪除了!
Sad,沒有這種直接的api,不過,不起眼的reindex卻承擔起這個重任,一開始我以爲跟這個api應該沒有關係,不只不省空間,反而還翻倍,直到發現能夠按條件reindex後,眼前一亮!我按時間條件reindex不就行了嘛~~
# 將 2019-01 月的文檔, 所有 reindex 到 knight-log.2019-01 中
curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "knight-log",
"type": "your_doc_type",
"query": {
"bool": {
"filter": {
"range": {
"day": {
"gte": "2019-01-01",
"lt": "2019-02-01"
}
}
}
}
}
},
"dest": {
"index": "knight-log.2019-01"
}
}
'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
tips1: 在reindex到新index以前,爲了加快索引,能夠對新index設置,常規的批量索引設置注意項,這裏也適用,如:replicas=0,refresh_interval=-1
tips2:第一次使用reindex,沒有開啓slice(至關於多線程),io和cpu雖然都不高,但很慢,可適當開啓,但注意slice不要超過index的shard數,看官方文檔,此處很少提!
tips3:若是一個月的文檔數太多,不放心一次性操做,你能夠嘗試先按天reindex到新月份index中,這樣能夠觀察cup和io,而後適當調整reindex相關參數後,再把當月餘下天數的文檔所有reindex到新index中
tips4:當進行完一次reindex,恢復 refresh_interval 後,能夠對新舊索引同時查詢,看文檔數是否一致!確保無異常!
tips5:手誤執行reindex,能夠中途取消本次task
將 knight-log 的replicas數從默認的1 變爲 0,這樣整個index省掉一半大小(非必須,剩餘磁盤多能夠忽略),而後,對 knight-log 按月份進行reindex,這樣反覆操做幾回,可完成近幾月的備份,形如 knight-log.2019-01,knight-log.2018-12 等(不要忘記2月的舊文檔哦,一樣須要reindex,這樣2月的index纔會完整),同時由於模板的特性,月份索引會自動加上 alias-knight-log 這個別名,一箭雙鵰!
reindex完必要的數據後(如近3月),就能夠直接刪掉 knight-log!
若幾個月後,磁盤又滿了,就能夠將最久老月份的index直接物理刪除,方便快捷!
5. 總結
追根究底,這種問題就不該該存在!可能一開始使用es不熟吧,起碼按天/月索引是應該具有的基本sense吧,你們要避免這個坑!
其次,若是單條文檔體積較大,能夠考慮將數據存入HBase,es中只存docId便可,經過條件搜出對應的docId,而後用docId去HBase取數據,進一步高效利用es,這經過禁用_source字段(保存原文檔的json字段)實現。不過,由於es只存倒排索引,因此沒了原文檔,會缺失部分便捷性,慎重考慮!
說回來,es不支持將大的索引按時間拆分紅小索引,只能在多個api組合中找到最優實踐,對應用來講,須要改動索引名後重啓,已是最小化改動,在整個過程當中,es保障了正常提供服務,不影響其餘業務。 經歷過上述操做,更加熟練的掌握了es多個相關api,瞭解查詢原理,寫入原理,分片分配策略以及查詢語句優化等知識點。另: kibana下使用dev tools也很是爽,會彈提示,算是發現的彩蛋,建議多用! ———————————————— 版權聲明:本文爲CSDN博主「Norman_Hu」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。 原文連接:https://blog.csdn.net/Norman_Hu/article/details/89917569