Elastic Search 不停服重建索引
問題:
在使用Elastic Search 過程中,可能經常會碰到需要修改 mapping的情況,如果是新增欄位,還算比較簡單,只需要在原來的mapping基礎之上再新增欄位即可。 但是如果碰到要修改原來欄位的屬性,就會比較棘手了, 因為ES並不支援在原來的mapping基礎上修改欄位的屬性。 這種情況能做的選擇就是重新建一份索引。
那麼如何重建索引呢。 最直觀的就是直接從資料來源從新導一份資料進入ES裡邊,但是這個是一個費時費力的操作。 可喜的是, ES給我們提供了一個 Reindex 的API,能夠直接從原來的index重建一份新的索引。由於ES裡邊本來就儲存了一份源資料,從這份源資料重新一份索引相對來說是一個比較快速的選擇。
但是,在使用ES的Reindex API的時候,還有一個問題必須要考慮。 官方文件中說明,Reindex的時候它是取了一份老的index的快照,這樣如果在停服的情況下沒什麼問題,但是如果在做重新索引的過程中, 仍舊有資料一直在更新,如何處理這部分更新的增量資料。
當然,辦法是有的,就是需要利用好ES提供的Reindex API的各種引數。
要做到不停服重建索引的前提,就是我們使用的索引已經建立了別名。比如我們建立的索引叫 index_v1 ,現在我們要重建新的索引 index_v2。 如果在沒使用別名的情況下,我們的邏輯程式碼中直接訪問了 index_v1, 那麼在重建索引之後,我們還必須更改程式碼邏輯,並且上線, 這是一個費時並且有延遲的過程。
如果我們給老的 index_v1 建立了一個別名 index_alias, 程式中也是直接訪問的這個別名。 在新建索引完成之後 ,就只需要將這個別名指向新的索引 index_v2 即可, 程式碼層面無需做任何的修改,而且是即時生效的。
解決辦法:
接下去就需要討論如何做到不停服的重建索引。 我們可以舉例說明。仿照官方文件的例子, 假設我們需要將老的索引 twitter 重建索引導 new_twitter。
- 第一步,當然是新建新的索引, 將mapping欄位都設定成想要的格式,
- 第二步,利用ES的API重建新的索引, 示例如下
curl -H "Content-Type:application/json" -X POST 'http://127.0.0.1:9200/_reindex' -d '{ "conflicts": "proceed", "source": { "index": "twitter" }, "dest": { "index": "new_twitter", "version_type": "external" } }'
可以比較一下最簡單的重建索引的方式:
curl -H "Content-Type:application/json" -X POST 'http://127.0.0.1:9200/_reindex' -d '{ "source": { "index": "twitter" }, "dest": { "index": "new_twitter", } }'
主要的區別就在這些引數上邊,我們一項項說明。
conflicts設定成proceed表示在重建索引的過程中,如果碰到源索引和目標索引同時存在相同的資料的時候,即發生資料衝突的時候,忽略衝突繼續向下執行。
version_type設定成external表示從源索引拷貝資料到目標索引的過程中,會把源索引中的版本號version帶上;如果發現目標索引資料version版本好比源索引的要小,就會執行更新的過程; ES預設的version_type為internal,會直接覆蓋目標索引的資料
當然, 在重建索引的過程,我們還可以用query指定過濾條件,只將一部分資料更新到目標索引 -
重建索引之後,我們可以修改源索引和目標索引的別名,將原來的別名指向新的索引,這樣所有更新的操作就會從新的索引出去。
-
接下去只剩最後一個問題,如果將重建索引的這段時間內,舊索引的一些更新操作增量同步到新的索引。其實很好解決,只需要我們在重新做一個重建索引的操作。 由於我們設定了version_type=external, 在碰到沒有更新的索引的時候,目標索引並不會做更新。如果碰到了有更新的索引,那麼他在源索引的版本號version是比目標索引裡邊的大的, 這樣就會對目標索引的資料做一個更新的操作
至此,我們就解決了如何不停服的進行資料索引重建的過程。重新操作之後的結果,會返回更新了多少個文件:
{
"took":88,
"timed_out":false,
"total":313,
"updated":1,
"created":0,
"deleted":0,
"batches":1,
"version_conflicts":312,
"noops":0,
"retries":{
"bulk":0,
"search":0
},
"throttled_millis":0,
"requests_per_second":-1,
"throttled_until_millis":0,
"failures":[
]
}
當然, 如果在重建索引的過程, 發現了問題,我們也可以終止重建索引的過程。我們可以通過
GET _tasks?detailed=true&actions=*reindex
查詢索引重建的進度並活動重建過程的任務id,通過這個id我們能對他進行取消任務的操作
POST _tasks/task_id:1/_cancel