背景
前段時間客戶根據(jù)看到的ES日志報了索引無法創(chuàng)建的Bug,研發(fā)工作完成差不多后,開始梳理前線客戶Bug,調(diào)研后才發(fā)現(xiàn)原來是Dangling索引的問題;這篇文檔算是對Dangling Indices知識的簡短梳理。ES(5.6.4)日志的記錄如下:
[2020-09-17T01:51:05,715][WARN ][o.e.g.DanglingIndicesState] [elasticsearch1] [[filebeat-2020.08.11/Uwim4o6nREed8_It7vBQ7A]] can not be imported as a dangling index, as index with same name already exists in cluster metadata
上述WARN信息,來源于ES的DanglingIndicesState類的findNewDanglingIndices(...)方法,如下:
/**
* Finds new dangling indices by iterating over the indices and trying to find indices
* that have state on disk, but are not part of the provided meta data, or not detected
* as dangled already.
*/
Map<Index, IndexMetaData> findNewDanglingIndices(final MetaData metaData) {
final Set<String> excludeIndexPathIds = new HashSet<>(metaData.indices().size() + danglingIndices.size());
for (ObjectCursor<IndexMetaData> cursor : metaData.indices().values()) {
excludeIndexPathIds.add(cursor.value.getIndex().getUUID());
}
excludeIndexPathIds.addAll(danglingIndices.keySet().stream().map(Index::getUUID).collect(Collectors.toList()));
try {
final List<IndexMetaData> indexMetaDataList = metaStateService.loadIndicesStates(excludeIndexPathIds::contains);
Map<Index, IndexMetaData> newIndices = new HashMap<>(indexMetaDataList.size());
final IndexGraveyard graveyard = metaData.indexGraveyard();
for (IndexMetaData indexMetaData : indexMetaDataList) {
if (metaData.hasIndex(indexMetaData.getIndex().getName())) {
logger.warn("[{}] can not be imported as a dangling index, as index with same name already exists in cluster metadata",
indexMetaData.getIndex());
} else if (graveyard.containsIndex(indexMetaData.getIndex())) {
logger.warn("[{}] can not be imported as a dangling index, as an index with the same name and UUID exist in the " +
"index tombstones. This situation is likely caused by copying over the data directory for an index " +
"that was previously deleted.", indexMetaData.getIndex());
} else {
logger.info("[{}] dangling index exists on local file system, but not in cluster metadata, " +
"auto import to cluster state", indexMetaData.getIndex());
newIndices.put(indexMetaData.getIndex(), indexMetaData);
}
}
return newIndices;
} catch (IOException e) {
logger.warn("failed to list dangling indices", e);
return emptyMap();
}
}
從WARN信息中可以得到兩點(diǎn)信息:
- 存在Dangling indices
- 因?yàn)橹孛膯栴},ES無法向正常的處理Dangling indices那樣,處理當(dāng)前的Dangling indices
結(jié)合ES集群重現(xiàn)上述問題的操作步驟:
- 搭建1master與1data的ES集群
- 創(chuàng)建索引(假定為my_index),并寫入少量數(shù)據(jù)
- 使用cp將data節(jié)點(diǎn)的nodes/0/indices路徑下my_index的目錄拷貝走
- 對my_index執(zhí)行DELETE操作(或手工刪除master、data節(jié)點(diǎn)的data目錄中的數(shù)據(jù),然后重啟master與data)
- 重復(fù)第2步操作,并將原my_index目錄拷貝到data節(jié)點(diǎn)下的nodes/0/indices目錄中
- 重啟data節(jié)點(diǎn)
- 這時data節(jié)點(diǎn)就會報出上述WARN信息
Dangling Indices
Dangling indices(懸空索引)指數(shù)據(jù)存儲在一個或多個節(jié)點(diǎn)磁盤上但當(dāng)前集群的clusterMetaData中并不包含這些索引信息。ES數(shù)據(jù)節(jié)點(diǎn)的啟動會首次從dataPath路徑下加載這些索引數(shù)據(jù),然后master能夠獲取到這些索引數(shù)據(jù)。Dangling indices通常是由以下幾種情況產(chǎn)生的:
- 當(dāng)有數(shù)據(jù)結(jié)點(diǎn)處于offline狀態(tài),而此時通過DELETE操作刪除索引,刪除的索引數(shù)大于集群設(shè)置的tombstones數(shù)量(默認(rèn)為500),然后該數(shù)據(jù)節(jié)點(diǎn)啟動并重新加入集群
-- DELETE操作將索引信息從clusterMetaData中刪除,而索引的真實(shí)數(shù)據(jù)在磁盤中 - 可能是因?yàn)樵技簛G失了其所有主節(jié)點(diǎn)的原因,原始集群中的某個節(jié)點(diǎn)添加到另一個集群中
添加到另一個集群的節(jié)點(diǎn),數(shù)據(jù)真實(shí)存儲在節(jié)點(diǎn)中,但新集群的clusterMetaData中不包含這些索引數(shù)據(jù)的信息 - 對于集群的數(shù)據(jù)節(jié)點(diǎn)來說,可能是從備份中還原了老的、舊的索引文件
- 集群丟失了所有主節(jié)點(diǎn),并且從備份中還原了這些主節(jié)點(diǎn),但是備份中的主節(jié)點(diǎn)不包含這些索引信息
-- 同樣是節(jié)點(diǎn)存儲著索引數(shù)據(jù),但主節(jié)點(diǎn)維護(hù)的clusterMetaData中不包含這些索引信息
分析源碼可知,ES對Dangling Indices的處理策略是首先會去尋找并判定數(shù)據(jù)節(jié)點(diǎn)中的哪些索引屬于Dangling狀態(tài),然后組裝好這些Indices,最后將這些Dangling Indices發(fā)送給master等待著后續(xù)的Allocation操作。上述的findNewDanglingIndices(...)函數(shù)主要職責(zé)就是尋找并判定處于Dangling狀態(tài)的索引,可以看到當(dāng)一個索引本身是Dangling Index,但在判定過程中發(fā)現(xiàn)clusterMetaData中已經(jīng)存在與當(dāng)前Dangling索引完全一樣名稱的索引時,則會報出WARN:can not be imported as a dangling index...;即盡管是Dangling indices,但因?yàn)榇嬖谂cclusterMetaData中重名的緣故,因此ES自身不能像處理正常Dangling indices那樣來處理此索引。ES會選擇放棄這類Dangling indices的處理
對于這些重名的Dangling indices,查閱了一些資料,發(fā)現(xiàn)并沒有比較好的方式來處理;ES官方討論區(qū)推薦的方式為要么刪除Dangling indices;要么是刪除ES中已經(jīng)存在的與Dangling狀態(tài)同名的索引,別的也沒有比較好的方式;



小結(jié)
總結(jié)下來,處理重名的Dangling indices的方式主要有如下三種:
- 刪除存儲在磁盤上的Dangling indices(一定的數(shù)據(jù)丟失)
- 刪除已存儲在ES中的重名索引(一定的數(shù)據(jù)丟失)
- 對已存儲在ES中的索引進(jìn)行rename操作,然后由ES正常處理Dangling indices(操作上繁瑣一些)
其實(shí)最好的方式應(yīng)該是盡可能的規(guī)避這個問題的發(fā)生,通過調(diào)研客戶環(huán)境發(fā)現(xiàn)其ES集群非常不穩(wěn)定,經(jīng)常會出現(xiàn)節(jié)點(diǎn)卡死并帶有重啟的操作;所以對此處理的策略是依據(jù)處理的數(shù)據(jù)量做好ES集群的規(guī)劃,包括master、data節(jié)點(diǎn)的部署劃分、依據(jù)ES能力進(jìn)行正常的寫入與搜索等操作。盡可能減少或避免繁重的且會導(dǎo)致ES卡死的操作,進(jìn)而避免ES節(jié)點(diǎn)頻繁的重啟發(fā)生。