據(jù)清理實(shí)戰(zhàn):Shell腳本與索引生命周期管理)
如果你在搜索引擎里敲下“shell刪除es數(shù)據(jù)”八成是遇到兩種情況要么ES集群磁盤(pán)告警要騰空間要么測(cè)試環(huán)境里灌了一堆臟數(shù)據(jù)想快速清理。別看這個(gè)需求聽(tīng)起來(lái)簡(jiǎn)單實(shí)際操作中翻車(chē)的人不少。有人用一條DELETE /*把整個(gè)集群的索引全刪了有人辛辛苦苦跑了半天_delete_by_query卻發(fā)現(xiàn)磁盤(pán)空間一點(diǎn)沒(méi)少還有人直接rm掉了ES的數(shù)據(jù)目錄導(dǎo)致節(jié)點(diǎn)起不來(lái)。這篇文章不打算復(fù)讀官方文檔而是把我實(shí)際處理ES數(shù)據(jù)清理時(shí)的完整思路、能直接抄的shell腳本以及踩過(guò)的坑一次性講清楚。適合運(yùn)維、后端開(kāi)發(fā)和所有需要跟ES打交道的人。1. 先分清你要?jiǎng)h的是“索引”還是“文檔”兩種清理思路差別很大很多人第一次接觸ES清理時(shí)腦子里想的還是MySQL那種DELETE FROM table WHERE ...的邏輯。但ES的存儲(chǔ)架構(gòu)決定了刪文檔和刪索引是兩條完全不同的路線性能和底層行為也天差地別。1.1 為什么說(shuō)刪除文檔不等于釋放空間ES底層是Lucene數(shù)據(jù)是按“段segment”組織的。段文件一旦寫(xiě)入就是只讀的后續(xù)的更新刪除操作不會(huì)直接改動(dòng)段文件而是靠標(biāo)記刪除.del文件來(lái)記錄。這么說(shuō)吧刪除一條文檔其實(shí)只是給這條數(shù)據(jù)打了一個(gè)墓碑標(biāo)記它占的空間還在那里。真正的空間釋放要等段合并merge發(fā)生把有刪除標(biāo)記的段文件重寫(xiě)一遍去掉那些死數(shù)據(jù)才可能回收空間。但merge本身需要額外資源ES不會(huì)因?yàn)槟銊h了幾條小數(shù)據(jù)就去觸發(fā)全局merge。這就是為什么你跑了delete_by_query返回的一堆任務(wù)都顯示成功但磁盤(pán)剩余空間幾乎沒(méi)變化的根本原因。反觀刪除索引它是直接把整個(gè)索引的全部分片文件都unreference掉數(shù)據(jù)段文件從磁盤(pán)上徹底摘除。在大多數(shù)情況下ES刪除索引后空間會(huì)立即釋放或者很快進(jìn)入可復(fù)用狀態(tài)。我對(duì)這個(gè)問(wèn)題的態(tài)度很簡(jiǎn)單如果只是要清理過(guò)期數(shù)據(jù)刪索引永遠(yuǎn)優(yōu)先于刪文檔除非業(yè)務(wù)對(duì)“索引還得保留”這件事有硬性要求必須按條件剔除部分?jǐn)?shù)據(jù)。1.2 什么時(shí)候該刪整索引什么時(shí)候該按條件刪我平時(shí)判斷的標(biāo)準(zhǔn)大概是這樣清理場(chǎng)景推薦手段原因日志型索引過(guò)期logstash-2024.01.01刪除整索引索引維度天然按時(shí)間段切分刪起來(lái)干凈利落空間釋放徹底同一索引內(nèi)混有多個(gè)業(yè)務(wù)的數(shù)據(jù)只需清掉某一小塊按查詢刪除_delete_by_query無(wú)法按索引隔離只能按條件刪但要接受“空間不是立即釋放”測(cè)試環(huán)境灌了一堆隨機(jī)數(shù)據(jù)刪除整索引并重建比delete_by_query快得多還能順便重置mapping需要保留索引結(jié)構(gòu)但刪除其中大量歷史數(shù)據(jù)新建索引 reindex然后切換別名刪索引再重建比在原索引上delete_by_query更可控看了這個(gè)表格你應(yīng)該能理解刪整個(gè)索引是ES親兒子操作delete_by_query更多是無(wú)奈之舉。千萬(wàn)不要圖省事在日志型索引里執(zhí)行按查詢刪除那相當(dāng)于“用大炮打蚊子”既慢又費(fèi)資源。2. 動(dòng)手前的三道“安全鎖”備份、權(quán)限、影響面確認(rèn)我見(jiàn)過(guò)最離譜的一次事故就是某運(yùn)維同學(xué)連備份都沒(méi)做直接按通配符刪了索引結(jié)果發(fā)現(xiàn)通配符寫(xiě)窄了一位日期刪的數(shù)據(jù)比預(yù)期多了一個(gè)月。所以不管你有多急下面三步別跳過(guò)。2.1 先看一眼集群健康再把快照備好動(dòng)手前先確認(rèn)集群狀態(tài)是不是健康的。curl -s http://localhost:9200/_cluster/health?pretty | jq .status返回green才能放心干。如果返回yellow意味著一定有副本分片沒(méi)分配這時(shí)候刪除索引會(huì)讓問(wèn)題更復(fù)雜。如果返回red那更要先解決深層次問(wèn)題別急著清理數(shù)據(jù)。接著做快照。已經(jīng)注冊(cè)過(guò)快照倉(cāng)庫(kù)的話直接執(zhí)行curl -X PUT http://localhost:9200/_snapshot/backup_repo/snapshot_before_cleanup_$(date %Y%m%d%H%M) \ -H Content-Type: application/json \ -d {indices:app-log-2024.01.*}沒(méi)注冊(cè)過(guò)倉(cāng)庫(kù)的話先注冊(cè)一個(gè)本地文件系統(tǒng)倉(cāng)庫(kù)curl -X PUT http://localhost:9200/_snapshot/backup_repo \ -H Content-Type: application/json \ -d {type:fs,settings:{location:/data/es_snapshots}}快照耗時(shí)跟數(shù)據(jù)量成正比。幾十GB的索引一般也就幾分鐘到十幾分鐘值得等。哪怕是測(cè)試環(huán)境我也建議快照做一遍再開(kāi)刪至少給自己留條后路。2.2 權(quán)限和索引匹配半徑的確認(rèn)如果ES集群開(kāi)了安全認(rèn)證用curl請(qǐng)求時(shí)要帶用戶信息curl -s -u elastic:yourpassword http://localhost:9200/_cat/indices?v刪索引需要delete_index權(quán)限執(zhí)行_delete_by_query需要對(duì)應(yīng)的索引寫(xiě)權(quán)限。實(shí)際工作中遇到最多的就是權(quán)限不足導(dǎo)致返回403排查起來(lái)又慢又煩。所以先隨手驗(yàn)證一下你當(dāng)前賬號(hào)能不能刪curl -s -u elastic:yourpassword -X DELETE http://localhost:9200/app-log-2024.01.15如果是403別掙扎了去找管理員給權(quán)限。注意刪除動(dòng)作不可逆建議新建一個(gè)只讀賬號(hào)先做查詢驗(yàn)證確認(rèn)清楚再換有權(quán)限的賬號(hào)執(zhí)行刪除。這里還有一個(gè)容易忽略的點(diǎn)。ES有很多點(diǎn)號(hào)開(kāi)頭的系統(tǒng)索引比如.kibana_*、.security-*、.monitoring-es-*。這些索引被隱藏保護(hù)但通配符包含.開(kāi)頭時(shí)也可能被誤匹配到。你手動(dòng)刪除時(shí)覺(jué)得自己寫(xiě)的是業(yè)務(wù)前綴萬(wàn)一前綴寫(xiě)錯(cuò)成了*系統(tǒng)索引很難閃開(kāi)。2.3 通配符殺傷半徑寧可多打幾次列出命令ES的索引通配符設(shè)計(jì)得很強(qiáng)大*、?都可以用。但也正因?yàn)閺?qiáng)大它成了誤刪事故的頭號(hào)來(lái)源。我養(yǎng)成一個(gè)習(xí)慣執(zhí)行刪除前先用通配符把所有會(huì)匹配到的索引列出來(lái)看一遍。curl -s http://localhost:9200/_cat/indices/app-log-2024.01.*?hindex,store.size,docs.countformatjson只看名字不過(guò)癮還可以加jq過(guò)濾把點(diǎn)開(kāi)頭的系統(tǒng)索引排除掉curl -s http://localhost:9200/_cat/indices?formatjson \ | jq -r .[] | select(.index | startswith(.) | not) | [.index, .store.size] | tsv這一步花不了半分鐘但能避免所有關(guān)于“到底匹配到了什么”的焦慮。確認(rèn)無(wú)誤后再走刪除動(dòng)作。那些敢直接執(zhí)行DELETE /*的同學(xué)一旦在生產(chǎn)集群上跑出來(lái)那種驚悚感會(huì)讓你終身難忘。3. 按索引刪除一個(gè)能上生產(chǎn)環(huán)境的清理腳本清了思路也加了安全鎖接下來(lái)進(jìn)入正題寫(xiě)一個(gè)真正能用的shell清理腳本。3.1 先手動(dòng)刪單個(gè)索引與通配符單個(gè)索引直接刪命令簡(jiǎn)單到不像話curl -s -u elastic:yourpassword -X DELETE http://localhost:9200/app-log-2024.01.15批量用通配符的情況比如要?jiǎng)h掉2024年1月所有日志索引curl -s -u elastic:yourpassword -X DELETE http://localhost:9200/app-log-2024.01.*這條命令會(huì)刪除所有匹配的索引。ES的通配符默認(rèn)會(huì)匹配所有分片狀態(tài)為open的索引正常業(yè)務(wù)下足夠了。不過(guò)我嚴(yán)重不建議你在生產(chǎn)環(huán)境直接這么干除非你已經(jīng)百分之百確認(rèn)過(guò)匹配范圍。更穩(wěn)妥的是用下面的腳本先算清楚再刪。3.2 按保留天數(shù)批量清理腳本邏輯詳解生產(chǎn)環(huán)境最常見(jiàn)的需求就一句話app-log-開(kāi)頭的索引只保留最近7天之前的一律刪除。下面這個(gè)腳本我一直在用邏輯不算復(fù)雜但把名字解析、系統(tǒng)索引過(guò)濾、dry-run都考慮進(jìn)去了。#!/usr/bin/env bash set -euo pipefail ES_HOST${ES_HOST:-http://localhost:9200} ES_USER${ES_USER:-elastic} ES_PASS${ES_PASS:-yourpassword} INDEX_PREFIX${INDEX_PREFIX:-app-log-} RETAIN_DAYS${RETAIN_DAYS:-7} DRY_RUN${DRY_RUN:-1} # 默認(rèn)打印不刪除確認(rèn)沒(méi)問(wèn)題再置0 # 取當(dāng)前時(shí)間戳 cutoff_timestamp$(date -d -${RETAIN_DAYS} days %s) # 用creation.date來(lái)判斷比解析索引名更穩(wěn) curl -s -u $ES_USER:$ES_PASS \ $ES_HOST/${INDEX_PREFIX}*/_settings?flat_settingstrueexpand_wildcardsopen \ | jq -r to_entries[] | select(.key | startswith(.) | not) | [.key, .value[index.creation_date]] | tsv \ | while IFS$\t read -r idx created_date_ms; do created_ts$((created_date_ms / 1000)) if (( created_ts cutoff_timestamp )); then if [[ $DRY_RUN 1 ]]; then echo [DRY RUN] would delete index: $idx else echo [DELETE] deleting index: $idx curl -s -u $ES_USER:$ES_PASS -X DELETE $ES_HOST/$idx echo fi else echo [KEEP] index: $idx fi done echo Cleanup job finished.腳本里幾個(gè)值得琢磨的點(diǎn)為什么用index.creation_date而不是解析索引名里的日期因?yàn)樗饕灰欢ㄒ?guī)整有的人用點(diǎn)分隔有的人用橫杠分隔還有人干脆不帶日期。creation_date是ES創(chuàng)建索引時(shí)打的時(shí)間戳不會(huì)騙人判斷是否超期更可靠。為什么開(kāi)頭就置DRY_RUN1先跑一遍看它打印哪些[DRY RUN] would delete index人工核對(duì)無(wú)誤再設(shè)置DRY_RUN0執(zhí)行真實(shí)刪除。這個(gè)習(xí)慣幫我擋掉了很多次“本來(lái)只想刪30天前結(jié)果把昨天的也算進(jìn)了刪除范圍”的烏龍。如果你確定索引名里就是標(biāo)準(zhǔn)日期格式也可以直接用索引名解析速度更快。下面這個(gè)函數(shù)兼容app-log-2024.01.15和app-log-2024-01-15兩種分隔方式parse_ts_from_name() { local name$1 local date_part${name#$INDEX_PREFIX} date_part${date_part//./-} date -d $date_part %s 2/dev/null || return 1 }把它替換掉腳本中的creation_date判斷也可以。不過(guò)我還是更推薦前者少繞彎子。3.3 實(shí)測(cè)刪除后磁盤(pán)空間多久釋放我在一套模擬的日志集群上做過(guò)測(cè)試索引總量80GB左右一共120個(gè)分片分布在三臺(tái)數(shù)據(jù)節(jié)點(diǎn)上。執(zhí)行完刪除命令后我從_cat/allocation觀察磁盤(pán)變化curl -s http://localhost:9200/_cat/allocation?v結(jié)果是這樣的刪除命令返回后每個(gè)節(jié)點(diǎn)上對(duì)應(yīng)的分片文件開(kāi)始清理。大約幾十秒到一兩分鐘磁盤(pán)剩余空間明顯回升。這說(shuō)明刪除整索引的空間釋放基本是即時(shí)的不需要額外觸發(fā)merge。但有一點(diǎn)要注意如果集群有自動(dòng)rebalance默認(rèn)開(kāi)啟某些節(jié)點(diǎn)的分片會(huì)重新平衡空間占用會(huì)有波動(dòng)。比如你刪了A節(jié)點(diǎn)上的大索引B節(jié)點(diǎn)可能馬上遷過(guò)來(lái)幾個(gè)分片導(dǎo)致看起來(lái)空間并沒(méi)有完全騰出來(lái)這是正?,F(xiàn)象。4. 按查詢條件刪數(shù)據(jù)_delete_by_query的完整用法與調(diào)優(yōu)總有一些數(shù)據(jù)沒(méi)辦法按索引整體刪除比如用戶誤產(chǎn)生了大量錯(cuò)誤文檔或者測(cè)試數(shù)據(jù)混進(jìn)了核心索引但你又不想傷及整個(gè)索引。這時(shí)就要用_delete_by_query。4.1 同步刪與異步刪小任務(wù)和大任務(wù)要分開(kāi)處理最基本的請(qǐng)求是這樣curl -s -u elastic:yourpassword -X POST \ http://localhost:9200/app-log-2024.01/_delete_by_query \ -H Content-Type: application/json \ -d { query: { range: { timestamp: { lt: 2024-01-10T00:00:00 } } } }小數(shù)據(jù)量幾百M(fèi)B內(nèi)這么干沒(méi)問(wèn)題請(qǐng)求會(huì)同步等待完成返回后你能直接看到刪除了多少條。但如果是幾十GB甚至更大的數(shù)據(jù)量同步等待會(huì)卡住HTTP連接經(jīng)常超時(shí)。這時(shí)候要改成異步模式curl -s -u elastic:yourpassword -X POST \ http://localhost:9200/app-log-2024.01*/_delete_by_query?wait_for_completionfalsescroll_size5000slices4 \ -H Content-Type: application/json \ -d { query: { range: { timestamp: { lt: now-7d } } } } \ | jq -r .task加了wait_for_completionfalse之后請(qǐng)求會(huì)立刻返回一個(gè)task id真正的刪除動(dòng)作在后臺(tái)執(zhí)行。返回的task id長(zhǎng)這樣{ task: oGuC7ABgQqO4Zb9CQ6DvVQ:123456 }拿這個(gè)task id你就可以隨時(shí)查看任務(wù)的執(zhí)行進(jìn)度。4.2 觀察任務(wù)進(jìn)度不是用直覺(jué)而是查_(kāi)tasks后臺(tái)任務(wù)跑起來(lái)之后總有同事問(wèn)“刪完了嗎”“怎么還在刪”。別看感覺(jué)直接查curl -s -u elastic:yourpassword http://localhost:9200/_tasks/oGuC7ABgQqO4Zb9CQ6DvVQ:123456?pretty | jq .task.statusstatus里會(huì)有這些關(guān)鍵字段total本次刪除需要處理的總文檔數(shù)updated更新過(guò)的文檔數(shù)deleted已經(jīng)刪除的文檔數(shù)batches完成了多少批version_conflicts版本沖突數(shù)noops空操作數(shù)按照deleted/total就能算出進(jìn)度條。還有更高階的curl -s -u elastic:yourpassword http://localhost:9200/_tasks?actions*delete*detailed | jq .nodes這條命令能列出集群內(nèi)所有正在執(zhí)行的刪除任務(wù)適合多任務(wù)并發(fā)時(shí)統(tǒng)一排查。4.3 Scroll、slices、refresh這幾個(gè)參數(shù)到底怎么設(shè)_delete_by_query底層靠scroll游標(biāo)分批掃數(shù)據(jù)再刪。參數(shù)沒(méi)設(shè)好輕則慢重則把集群拖垮。我調(diào)過(guò)不少集群常用經(jīng)驗(yàn)如下參數(shù)建議值理由scroll_size500010000默認(rèn)1000偏保守?cái)?shù)據(jù)量大時(shí)批次太小網(wǎng)絡(luò)交互次數(shù)太多slices24或auto并發(fā)切片能明顯加速但不建議超過(guò)索引分片總數(shù)開(kāi)太多反而調(diào)度開(kāi)銷(xiāo)大refreshfalsedelete_by_query默認(rèn)就是false不用額外設(shè)置但你要知道它不會(huì)像普通刪單條那樣觸發(fā)refreshconflictsproceed慎用有并發(fā)寫(xiě)入時(shí)大概率出現(xiàn)版本沖突proceed會(huì)跳過(guò)沖突繼續(xù)刪但可能導(dǎo)致部分文檔漏刪requests_per_second-1不限速生產(chǎn)集群謹(jǐn)慎不限速建議設(shè)置合理的值例如5001000給業(yè)務(wù)留點(diǎn)IO當(dāng)數(shù)據(jù)量真的很大比如索引本身已經(jīng)幾十上百GB我不會(huì)直接在原索引上跑_delete_by_query。正確做法是新建一個(gè)干凈索引用reindex把需要保留的數(shù)據(jù)遷過(guò)去然后切換別名最后刪除舊索引。這樣做的效率遠(yuǎn)高于在原索引上delete還能順便壓縮段文件。我的個(gè)人判斷閾值是這樣的刪除量小于索引總量的20%可以考慮_delete_by_query超過(guò)20%就值得走“reindex 替換別名”的路線。別嫌麻煩刪幾十GB的同段數(shù)據(jù)所消耗的IO和CPU比你想象的大得多。5. 定時(shí)自動(dòng)清理cron方案和ILM方案怎么選一次性清理做完還得考慮以后怎么防不然磁盤(pán)再過(guò)一個(gè)月又要報(bào)警。自動(dòng)清理有兩條路一條是shell cron簡(jiǎn)單直接另一條是ES自帶的ILMIndex Lifecycle Management更加優(yōu)雅。兩條路我都部署過(guò)說(shuō)下差異。5.1 cron shell適合“想盡快看結(jié)果”的清理把上一節(jié)的腳本保存到/opt/scripts/es_cleanup.sh加個(gè)cron任務(wù)0 3 * * * /opt/scripts/es_cleanup.sh /var/log/es_cleanup.log 21每天凌晨3點(diǎn)跑一次把過(guò)期索引刪掉日志記錄到/var/log/。選凌晨是因?yàn)闃I(yè)務(wù)低谷IO壓力小。cron方案的好處是直觀出了什么問(wèn)題看日志就行。壞處是它不知道ES的分片狀態(tài)也不了解數(shù)據(jù)生命周期。而且腳本里如果有bug第二天可能就把不該刪的也刪了。所以這個(gè)方案適合數(shù)據(jù)量可控、索引命名規(guī)律、不復(fù)雜的環(huán)境。5.2 ILM適合長(zhǎng)期穩(wěn)定寫(xiě)入的日志型索引ILM是ES官方的生命周期管理功能。定義一個(gè)30天后刪除的策略curl -X PUT http://localhost:9200/_ilm/policy/log_30d_policy \ -H Content-Type: application/json \ -d { policy: { phases: { hot: { min_age: 0d, actions: {} }, delete: { min_age: 30d, actions: { delete: {} } } } } }然后把這個(gè)策略掛到索引模板上后續(xù)新索引自動(dòng)套用curl -X PUT http://localhost:9200/_index_template/app-log-template \ -H Content-Type: application/json \ -d { index_patterns: [app-log-*], template: { settings: { index.lifecycle.name: log_30d_policy } } }ILM的優(yōu)勢(shì)很明顯不需要額外腳本ES自己會(huì)在分片層面執(zhí)行滾動(dòng)和刪除。缺點(diǎn)是策略本身有一定學(xué)習(xí)成本而且遇到已經(jīng)存在的歷史索引需要手動(dòng)POST /app-log-2024.01.01/_ilm/explain確認(rèn)它有沒(méi)有被正確應(yīng)用。我的建議是持續(xù)寫(xiě)入的日志型數(shù)據(jù)上ILM才是正路一次性清理歷史臟數(shù)據(jù)用shell腳本更順手。兩者并不矛盾可以同時(shí)存在。5.3 兜底寫(xiě)一個(gè)刪除前的“最后確認(rèn)”無(wú)論哪種自動(dòng)方式我都強(qiáng)烈建議加上一道“最后確認(rèn)”的兜底邏輯。在腳本里加一個(gè)環(huán)境變量ALLOW_DELETEif [[ ${ALLOW_DELETE:-0} ! 1 ]]; then echo Deletion is not allowed. Set ALLOW_DELETE1 to execute. exit 1 ficron任務(wù)默認(rèn)不傳這個(gè)變量所以即使腳本被誤觸發(fā)也只會(huì)打印日志而不真正刪除。只有人工手動(dòng)確認(rèn)后才會(huì)帶著ALLOW_DELETE1執(zhí)行。這個(gè)設(shè)計(jì)可能看起來(lái)多此一舉但在生產(chǎn)集群上救過(guò)我的命。6. 踩過(guò)的坑別踩第二遍6.1 誤刪全集群索引的一場(chǎng)“演練”有次某同事給測(cè)試環(huán)境寫(xiě)清理腳本想刪掉前綴為test-的索引結(jié)果命令寫(xiě)成這樣curl -X DELETE http://localhost:9200/test*這個(gè)寫(xiě)在命令里沒(méi)問(wèn)題問(wèn)題是當(dāng)時(shí)測(cè)試環(huán)境里其他團(tuán)隊(duì)也創(chuàng)建了testing-、test-data-等前綴的索引甚至還創(chuàng)建了.test-infra這種系統(tǒng)索引。通配符一下把所有匹配到的全刪了?;謴?fù)過(guò)程非常痛苦幸好有快照但復(fù)盤(pán)時(shí)有個(gè)問(wèn)題其實(shí)很容易提前規(guī)避在刪除命令里同時(shí)排除非業(yè)務(wù)前綴。我個(gè)人后來(lái)的約定是生產(chǎn)環(huán)境腳本里永遠(yuǎn)不用裸通配符刪索引只用腳本內(nèi)的INDEX_PREFIX拼接匹配范圍并且跑之前先打印所有要?jiǎng)h的索引名。6.2 rm -rf 數(shù)據(jù)目錄的嚴(yán)重性這個(gè)坑我必須重點(diǎn)提。某同學(xué)覺(jué)得ES索引刪得慢直接登錄數(shù)據(jù)節(jié)點(diǎn)把data/nodes/下的索引目錄rm -rf了。結(jié)果節(jié)點(diǎn)進(jìn)程還活著Lucene文件卻被物理刪除索引狀態(tài)直接變得一團(tuán)糟部分分片怎么都恢復(fù)不了最后還是靠快照才把數(shù)據(jù)追回來(lái)。ES的數(shù)據(jù)目錄堅(jiān)決不能直接動(dòng)。它跟普通文件完全不一樣Lucene在節(jié)點(diǎn)上維護(hù)著大量的文件句柄和內(nèi)存映射rm刪除之后進(jìn)程雖然不會(huì)立刻崩但后續(xù)寫(xiě)入操作會(huì)在底層數(shù)據(jù)不完整的情況下繼續(xù)運(yùn)行最終大概率導(dǎo)致集群red狀態(tài)。如果你真的遇到“刪不掉、磁盤(pán)要爆”的情況別走極端。優(yōu)先考慮關(guān)掉rebalance、先把數(shù)據(jù)遷到其他節(jié)點(diǎn)再刪除索引。如果集群已經(jīng)無(wú)法響應(yīng)重啟節(jié)點(diǎn)后再說(shuō)。6.3 刪除后副本遷移引發(fā)雪崩很多人不會(huì)想到這個(gè)刪除一個(gè)大索引后集群的自動(dòng)rebalance會(huì)被觸發(fā)。假設(shè)一個(gè)節(jié)點(diǎn)上原有一大片分片現(xiàn)在空出來(lái)了ES會(huì)把其他節(jié)點(diǎn)的分片遷移過(guò)來(lái)補(bǔ)充平衡。這本身是好事但如果一次性刪了多個(gè)大索引rebalance會(huì)在短時(shí)間內(nèi)大量遷移分片IOPS飆升反而拖慢了業(yè)務(wù)查詢。我的處理辦法是刪除大索引之前在集群范圍暫時(shí)關(guān)閉rebalancecurl -X PUT http://localhost:9200/_cluster/settings \ -H Content-Type: application/json \ -d { transient: { cluster.routing.rebalance.enable: none } }等刪除完成、分片狀態(tài)穩(wěn)定下來(lái)再恢復(fù)curl -X PUT http://localhost:9200/_cluster/settings \ -H Content-Type: application/json \ -d { transient: { cluster.routing.rebalance.enable: all } }實(shí)測(cè)這個(gè)方法能顯著減少刪除大索引后的集群波動(dòng)。7. 一點(diǎn)操作心得寫(xiě)到這里把我在實(shí)際中摸索出來(lái)的幾條心得分享一下。第一條還是那句老話能用刪索引解決的事千萬(wàn)不要用刪文檔解決。ES的段合并機(jī)制決定了文檔刪除的空間釋放是滯后的刪索引才是干凈利落的做法。第二條是把所有清理腳本都做成“默認(rèn)dry-run、顯式確認(rèn)才執(zhí)行”的模式。初期可能覺(jué)得多此一舉但當(dāng)你凌晨三點(diǎn)被報(bào)警電話吵醒半睡半醒之間還能靠這層機(jī)制避免事故的時(shí)候你就知道它有多重要。最后一個(gè)小技巧無(wú)論用哪種方式清理記得在清理結(jié)束后順手看一眼_cluster/health確認(rèn)集群狀態(tài)恢復(fù)為green。如果返回的是黃色大概率有副本分片還沒(méi)分配這時(shí)候別急著關(guān)監(jiān)控等幾分鐘再查一次。數(shù)據(jù)清理看似只是幾條curl命令的事但背后涉及索引生命周期、段合并、分片分配這些機(jī)制。摸熟了這些機(jī)制你再遇到ES磁盤(pán)告警就不會(huì)慌按本文的流程走有備份、有確認(rèn)、有腳本基本不會(huì)出大問(wèn)題。