建設(shè)網(wǎng)站各方面費(fèi)用預(yù)算網(wǎng)絡(luò)宣傳
一、索引拆分和收縮的場(chǎng)景
在Elasticsearch集群部署的初期我們可能評(píng)估不到位,導(dǎo)致分配的主分片數(shù)量太少,單分片的數(shù)據(jù)量太大,導(dǎo)致搜索時(shí)性能下降,這時(shí)我們可以使用Elasticsearch提供的Split功能對(duì)當(dāng)前的分片進(jìn)行拆分,拆分到具有更多主分片的新索引。
而相反的,在數(shù)據(jù)規(guī)模比較大的集群中,可能存在一個(gè)數(shù)據(jù)量很小,但是分片數(shù)量非常龐大的索引,而分片的管理依賴(lài)于Master節(jié)點(diǎn),一旦分片數(shù)量太大,將會(huì)降低集群的整體性能,故障恢復(fù)也更慢,這時(shí)候可以使用Elasticsearch提供的Shrink API降低分片數(shù)量。
二、索引拆分
2.1、索引拆分API和拆分邏輯
Elasticsearch提供了Split API,用于將索引拆分到具有更多主分片的新索引。Split API的格式如下:
POST /<index>/_split/<target-index>PUT /<index>/_split/<target-index>
要完成整個(gè)Split的操作需要滿(mǎn)足以下條件:
- 索引必須是只讀的。
- 集群的狀態(tài)必須是green。
- 目標(biāo)索引的主分片數(shù)量必須大于源索引的主分片數(shù)量。
- 處理索引拆分的節(jié)點(diǎn)必須有足夠的空閑磁盤(pán)空間來(lái)容納現(xiàn)有索引的第二個(gè)副本。
以下API請(qǐng)求可以將索引設(shè)置為只讀:
curl -X PUT "localhost:9200/my_source_index/_settings?pretty" -H 'Content-Type: application/json' -d'
{"settings": {"index.blocks.write": true }
}
'
如果當(dāng)前索引是是一個(gè)data stream的寫(xiě)索引,則不允許進(jìn)行索引拆分,需要對(duì)data stream進(jìn)行回滾,創(chuàng)建一個(gè)新的寫(xiě)索引,才可以對(duì)當(dāng)前索引進(jìn)行拆分。
以下是使用Split API進(jìn)行索引拆分的請(qǐng)求案例,Split
API支持settings
和aliases
。
curl -X POST "localhost:9200/my-index-000001/_split/split-my-index-000001?pretty" -H 'Content-Type: application/json' -d'
{"settings": {"index.number_of_shards": 2},"aliases": {"my_alias":{}}
}
'
index.number_of_shards
指定的主分片的數(shù)量 必須是源分片數(shù)量的倍數(shù)。
索引拆分可以拆分的分片的數(shù)量由參數(shù)index.number_of_routing_shards
決定,路由分片的數(shù)量指定哈??臻g,該空間在內(nèi)部用于以一致性哈希的形式在各個(gè) shard 之間分發(fā)文檔。 例如,將 number_of_routing_shards 設(shè)置為30(5 x 2 x 3)的具有5個(gè)分片的索引可以拆分為 以2倍 或 3倍的形式進(jìn)行拆分。換句話說(shuō),可以如下拆分:
5→10→30(拆分依次為2和3)
5→15→30(拆分依次為3和2)
5→30(拆分6)
index.number_of_routing_shards
是一個(gè)靜態(tài)配置,可以在創(chuàng)建索引的時(shí)候指定,也可以在關(guān)閉的索引上設(shè)置。其默認(rèn)值取決于原始索引中主分片的數(shù)量,默認(rèn)情況下,允許按2的倍數(shù)分割最多1024個(gè)分片。但是,必須考慮主碎片的原始數(shù)量。例如,使用5個(gè)主碎片創(chuàng)建的索引可以被分割為10、20、40、80、160、320,或最多640個(gè)碎片。
如果源索引只有一個(gè)主分片,那么可以被拆分成為任意數(shù)量的主分片。
2.2、索引拆分的工作過(guò)程
- 創(chuàng)建一個(gè)與源索引定義一樣的目標(biāo)索引,并且具有更多的主分片。(注意,是創(chuàng)建了一個(gè)新的索引,而并不是在源索引上擴(kuò)大分片)
- 將段(segment)從源索引硬鏈接到目標(biāo)索引。(如果文件系統(tǒng)不支持硬鏈接,那么所有的段都會(huì)被復(fù)制到新的索引中,這是一個(gè)非常耗時(shí)的過(guò)程。)
- 對(duì)所有的文檔進(jìn)行重新散列。
- 目標(biāo)索引進(jìn)行Recover。
2.3、為什么不支持在源索引上增加增量分片?
我們知道其實(shí)大多數(shù)的鍵值存儲(chǔ)都支持隨著數(shù)據(jù)的增長(zhǎng)實(shí)現(xiàn)自動(dòng)分片的自動(dòng)擴(kuò)展。但是為什么Elasticsearch不支持呢?
其實(shí)主要是因?yàn)镋lasticsearch的底層結(jié)構(gòu)和數(shù)據(jù)分布邏輯決定的,Elasticsearch需要使用一定的哈希的方法找到數(shù)據(jù)到底應(yīng)該存放在哪個(gè)分片,這就決定了如果新增一個(gè)分片,則需要使用不同的哈希方案重新平衡現(xiàn)有的數(shù)據(jù),那么整體的操作就會(huì)變得非常復(fù)雜。
其他鍵值存儲(chǔ)系統(tǒng)解決這個(gè)問(wèn)題的方案一般是使用一致性哈希,當(dāng)分片數(shù)從N增加到N+1時(shí),一致性哈希只需要對(duì)1/N的key進(jìn)行重新分配。但是Elasticsearch分片的本質(zhì)實(shí)際上是Lucene的索引,而從Lucene索引刪除一小部分的數(shù)據(jù),通常比鍵值存儲(chǔ)系統(tǒng)的成本要高得多。所以Elasticsearch選擇在索引層面上進(jìn)行拆分,使用硬鏈接進(jìn)行高效的文件復(fù)制,以避免在索引間移動(dòng)文檔。
對(duì)于僅追加數(shù)據(jù)而沒(méi)有修改、刪除等場(chǎng)景,可以通過(guò)創(chuàng)建一個(gè)新索引并將新數(shù)據(jù)推送到該索引,同時(shí)添加一個(gè)用于讀操作的涵蓋舊索引和新索引的別名來(lái)獲得更大的靈活性。假設(shè)舊索引和新索引分別有M和N個(gè)分片,這與搜索一個(gè)有M+N個(gè)分片的索引相比沒(méi)有任何開(kāi)銷(xiāo)。
2.4、如何監(jiān)控索引拆分的進(jìn)度
使用Split API進(jìn)行索引拆分,API正常返回并不意味著Split的過(guò)程已經(jīng)完成,這僅僅意味著創(chuàng)建目標(biāo)索引的請(qǐng)求已經(jīng)完成,并且加入了集群狀態(tài),此時(shí)主分片可能還未被分配,副本分片可能還未創(chuàng)建成功。
一旦主分片完成了分配,狀態(tài)就會(huì)轉(zhuǎn)化為initializing
,并且開(kāi)始進(jìn)行拆分過(guò)程,直到拆分過(guò)程完成,分片的狀態(tài)將會(huì)變成active
。
可以使用_cat recovery API
來(lái)監(jiān)控Split進(jìn)程,或者可以使用集群健康A(chǔ)PI通過(guò)將wait_for_status
參數(shù)設(shè)置為黃色來(lái)等待所有主分片分配完畢。
三、索引收縮
3.1、索引收縮API和收縮邏輯
對(duì)于索引分片數(shù)量,我們一般在模板中統(tǒng)一定義,在數(shù)據(jù)規(guī)模比較大的集群中,索引分片數(shù)一般也大一些,在我的集群中設(shè)置為 24。但是,并不是所有的索引數(shù)據(jù)量都很大,這些小數(shù)據(jù)量的索引也同樣有較大的分片數(shù)。在 elasticsearch 中,主節(jié)點(diǎn)管理分片是很大的工作量,降低集群整體分片數(shù)量可以降低 recovery 時(shí)間,減小集群狀態(tài)的大小。很多時(shí)候,冷索引不會(huì)再有數(shù)據(jù)寫(xiě)入,此時(shí),可以使用 shrink API 縮小索引分配數(shù)??s小完成后,源索引可刪除。
shrink API 是 ES5.0之后提供的新功能,他并不對(duì)源索引進(jìn)行操作,他使用與源索引相同的配置創(chuàng)建一個(gè)新索引,僅僅降低分片數(shù)。由于添加新文檔時(shí)使用對(duì)分片數(shù)量取余獲取目的分片的關(guān)系,原分片數(shù)量是新分片倍數(shù)。如果源索引的分片數(shù)為素?cái)?shù),目標(biāo)索引的分片數(shù)只能為1.
將現(xiàn)有索引縮小為具有更少主分片的新索引,一個(gè)索引要能夠被shrink進(jìn)行縮小,需要滿(mǎn)足以下三個(gè)條件:
- 索引是可讀的
- 索引中每個(gè)分片的副本必須位于同一個(gè)節(jié)點(diǎn)上。(注意,“所有分片副本”不是指索引的全部分片,無(wú)論主分片還是副分片,滿(mǎn)足任意一個(gè)就可以,分配器也不允許將主副分片分配到同一節(jié)點(diǎn)。所以可以是刪除了所有的副本分片,也可以是把所有的副本分片全部放在同一個(gè)節(jié)點(diǎn)上。)
- 索引的狀態(tài)必須為green
為了使分片分配更容易,可以先刪除索引的復(fù)制分片,等完成了shrink操作以后再重新添加復(fù)制分片。
可以使用以下代碼,實(shí)現(xiàn)刪除所有的副本分片,將所有的主分片分配到同一個(gè)節(jié)點(diǎn)上,并且設(shè)置索引狀態(tài)為只讀:
curl -X PUT "localhost:9200/my_source_index/_settings?pretty" -H 'Content-Type: application/json' -d'
{"settings": {"index.number_of_replicas": 0, "index.routing.allocation.require._name": "shrink_node_name", "index.blocks.write": true }
}
'
重新分配源索引的分片可能需要一段時(shí)間,可以使用_cat API跟蹤進(jìn)度,或者使用集群健康A(chǔ)PI通過(guò)wait_for_no_relocating_shards參數(shù)等待所有分片完成重新分配。
當(dāng)完成以上步驟以后就可以進(jìn)行shrink操作了,以下為_(kāi)shrink API的格式:
POST /<index>/_shrink/<target-index>PUT /<index>/_shrink/<target-index>
以下案例將索引my-index-000001縮小主分片到shrunk-my-index-000001索引。
curl -X POST "localhost:9200/my-index-000001/_shrink/shrunk-my-index-000001?pretty"
{"settings": {"index.number_of_replicas": 1,"index.number_of_shards": 1,"index.codec": "best_compression""index.routing.allocation.require._name": null, "index.blocks.write": null }"aliases": {"my_search_indices": {}}
}
收縮索引API允許您將現(xiàn)有索引收縮為主分片更少的新索引。目標(biāo)索引中請(qǐng)求的主分片數(shù)量必須是源索引中主分片數(shù)量的一個(gè)因子。例如,包含8個(gè)主碎片的索引可以收縮為4個(gè)、2個(gè)或1個(gè)主碎片,或者包含15個(gè)主碎片的索引可以收縮為5個(gè)、3個(gè)或1個(gè)主碎片。如果索引中的碎片數(shù)量是一個(gè)質(zhì)數(shù),那么它只能收縮為一個(gè)主分片。在收縮之前,索引中每個(gè)分片的一個(gè)(主或副本)副本必須存在于同一個(gè)節(jié)點(diǎn)上。
如果當(dāng)前索引是是一個(gè)data stream的寫(xiě)索引,則不允許進(jìn)行索引收縮,需要對(duì)data stream進(jìn)行回滾,創(chuàng)建一個(gè)新的寫(xiě)索引,才可以對(duì)當(dāng)前索引進(jìn)行收縮。
3.2、索引收縮的工作過(guò)程
整個(gè)索引收縮的過(guò)程如下:
- 創(chuàng)建一個(gè)新的目標(biāo)索引,其定義與源索引相同,但主分片的數(shù)量較少。
- 將段從源索引硬鏈接到目標(biāo)索引。(如果文件系統(tǒng)不支持硬鏈接,那么所有的段都被復(fù)制到新的索引中,這是一個(gè)非常耗時(shí)的過(guò)程。此外,如果使用多個(gè)數(shù)據(jù)路徑,不同數(shù)據(jù)路徑上的分片需要一個(gè)完整的段文件拷貝,如果它們不在同一個(gè)磁盤(pán)上,因?yàn)橛叉溄硬荒芸绱疟P(pán)工作)
- 恢復(fù)目標(biāo)索引
雖然Elasticsearch提供了Split和Shrink API,但是更建議的應(yīng)該是做好更好的索引創(chuàng)建前的評(píng)估工作,因?yàn)槭褂肧plit和Shrink都有一定的成本。