ES7.5 下動態擴大索引的shard數量

  • 2020 年 2 月 14 日
  • 筆記

在老版本的ES(例如2.3版本)中, index的shard數量定好後,就不能再修改,除非重建數據才能實現。

從ES6.1開始,ES 支援可以在線操作擴大shard的數量(注意:操作期間也需要對index鎖寫)

從ES7.0開始,split時候,不再需要加參數 index.number_of_routing_shards

具體參考官方文檔:

    https://www.elastic.co/guide/en/elasticsearch/reference/7.5/indices-split-index.html

    https://www.elastic.co/guide/en/elasticsearch/reference/6.1/indices-split-index.html

split的過程:

    1、創建一個新的目標index,其定義與源index相同,但是具有更多的primary shard。

    2、將segment從源index硬鏈接到目標index。(如果文件系統不支援硬鏈接,則將所有segment都複製到新索引中,這是一個非常耗時的過程。)

    3、創建低級文件後,再次對所有文檔進行哈希處理,以刪除屬於不同shard的documents

    4、恢複目標索引,就像它是剛剛重新打開的封閉索引一樣。

為啥ES不支援增量resharding?

    從N個分片到N + 1個分片。增量重新分片確實是許多鍵值存儲支援的功能。僅添加一個新的分片並將新的數據推入該新的分片是不可行的:這可能是一個索引瓶頸,並根據給定的_id來確定文檔所屬的分片,這對於獲取,刪除和更新請求是必需的,會變得很複雜。這意味著我們需要使用其他哈希方案重新平衡現有數據。

    鍵值存儲有效執行此操作的最常見方式是使用一致的哈希。當分片的數量從N增加到N + 1時,一致的哈希僅需要重定位鍵的1 / N。但是,Elasticsearch的存儲單位(碎片)是Lucene索引。由於它們以搜索為導向的數據結構,僅佔Lucene索引的很大一部分,即僅佔5%的文檔,將其刪除並在另一個分片上建立索引通常比鍵值存儲要高得多的成本。如上節所述,當通過增加乘數來增加分片數量時,此成本保持合理:這允許Elasticsearch在本地執行拆分,這又允許在索引級別執行拆分,而不是為需要重新索引的文檔重新編製索引移動,以及使用硬鏈接進行有效的文件複製。

    對於僅追加數據,可以通過創建新索引並將新數據推送到其中,同時添加一個別名來覆蓋讀取操作的新舊索引,從而獲得更大的靈活性。假設舊索引和新索引分別具有M和N個分片,與搜索具有M + N個分片的索引相比,這沒有開銷。

索引能進行split的前提條件:

    1、目標索引不能存在。

    2、源索引必須比目標索引具有更少的primary shard。

    3、目標索引中主shard的數量必須是源索引中主shard的數量的倍數。

    4、處理拆分過程的節點必須具有足夠的可用磁碟空間,以容納現有索引的第二個副本。

下面是具體的實驗部分:

tips:實驗機器有限,索引的replica都設置為0,生產上至少replica>=1

# 創建一個索引,2個主shard,沒有副本

curl -s -X PUT "http://1.1.1.1:9200/twitter?pretty" -H 'Content-Type: application/json' -d'  {    "settings": {      "index.number_of_shards": 2,      "index.number_of_replicas": 0    },      "aliases": {      "my_search_indices": {}    }  }'

# 寫入幾條測試數據

curl -s -X PUT "http://1.1.1.1:9200/my_search_indices/_doc/11?pretty" -H 'Content-Type: application/json' -d '{    "id": 11,    "name":"lee",    "age":"23"  }'  curl -s -X PUT "http://1.1.1.1:9200/my_search_indices/_doc/22?pretty" -H 'Content-Type: application/json' -d '{    "id": 22,    "name":"amd",    "age":"22"  }'

# 查詢數據

curl -s -XGET "http://1.1.1.1:9200/my_search_indices/_search" | jq .

# 對索引鎖寫,以便下面執行split操作

curl -s -X PUT "http://1.1.1.1:9200/twitter/_settings?pretty" -H 'Content-Type: application/json' -d '{    "settings": {      "index.blocks.write": true    }  }'

# 寫數據測試,確保鎖寫生效

curl -s -X PUT "http://1.1.1.1:9200/twitter/_doc/33?pretty" -H 'Content-Type: application/json' -d '{    "id": 33,    "name":"amd",    "age":"33"  }'

# 取消 twitter 索引的alias

curl -s -X POST "http://1.1.1.1:9200/_aliases?pretty" -H 'Content-Type: application/json' -d '{      "actions" : [          { "remove" : { "index" : "twitter", "alias" : "my_search_indices" } }      ]  }'

# 開始執行 split 切分索引的操作,調整後索引名稱為new_twitter,且主shard數量為8

curl -s -X POST "http://1.1.1.1:9200/twitter/_split/new_twitter?pretty" -H 'Content-Type: application/json' -d '{    "settings": {      "index.number_of_shards": 8,      "index.number_of_replicas": 0    }  }'

# 對新的index添加alias 

curl -s -X POST "http://1.1.1.1:9200/_aliases?pretty" -H 'Content-Type: application/json' -d '{      "actions" : [          { "add" : { "index" : "new_twitter", "alias" : "my_search_indices" } }      ]  }'

結果:

{

  "acknowledged" : true,

  "shards_acknowledged" : true,

  "index" : "new_twitter"

}

補充:

    查看split的進度,可以使用 _cat/recovery 這個api, 或者在 cerebro 介面上查看。

# 查看新索引的數據,能正常查看

curl -s -XGET "http://1.1.1.1:9200/my_search_indices/_search" | jq .

# 對新索引寫數據測試,可以看到失敗的

curl -s -X PUT "1.1.1.1:9200/my_search_indices/_doc/33?pretty" -H 'Content-Type: application/json' -d '{    "id": 33,    "name":"amd",    "age":"33"  }'

# 打開索引的寫功能

curl -s -X PUT "1.1.1.1:9200/my_search_indices/_settings?pretty" -H 'Content-Type: application/json' -d '{    "settings": {      "index.blocks.write": false     }  }'

# 再次對新索引寫數據測試,可以看到此時,寫入是成功的

curl -s -X PUT "1.1.1.1:9200/my_search_indices/_doc/33?pretty" -H 'Content-Type: application/json' -d '{    "id": 33,    "name":"amd",    "age":"33"  }'    curl -s -X PUT "1.1.1.1:9200/my_search_indices/_doc/44?pretty" -H 'Content-Type: application/json' -d '{    "id": 44,    "name":"intel",    "age":"4"  }'

# 此時,老的那個索引還是只讀的,我們確保新索引OK後,就可以考慮關閉或者刪除老的 twitter索引了。