背景
眾所周知氓皱,Elasticsearch是?個(gè)實(shí)時(shí)的分布式搜索引擎路召,為?戶提供搜索服務(wù)。當(dāng)我們決定存儲(chǔ)某種數(shù)據(jù)波材,在創(chuàng)建索引的時(shí)候就需要將數(shù)據(jù)結(jié)構(gòu)优训,即Mapping確定下來(lái),于此同時(shí)索引的設(shè)定和很多固定配置將不能改變各聘。
那如果后續(xù)業(yè)務(wù)發(fā)生變化,需要改變數(shù)據(jù)結(jié)構(gòu)或者更換ES更換分詞器怎么辦呢抡医?為此躲因,Elastic團(tuán)隊(duì)提供了很多通過輔助?具來(lái)幫助開發(fā)?員進(jìn)?重建索引的方案。
如果對(duì) reindex
API 不熟悉忌傻,那么在遇到重構(gòu)的時(shí)候大脉,必然事倍功半,效率低下水孩。反之镰矿,就可以方便地進(jìn)行索引重構(gòu),省時(shí)省力俘种。
步驟
假設(shè)之前我們已經(jīng)存在一個(gè)blog索引秤标,因?yàn)楦鼡Q分詞器需要對(duì)該索引中的數(shù)據(jù)進(jìn)行重建索引绝淡,以便支持業(yè)務(wù)使用新的分詞規(guī)則搜索數(shù)據(jù),并且盡可能使這個(gè)變化對(duì)外服務(wù)沒有感知苍姜,大概分為以下幾個(gè)步驟:?
- 新增?個(gè)索引
blog_lastest
牢酵,Mapping數(shù)據(jù)結(jié)構(gòu)與blog
索引一致 - 將
blog
數(shù)據(jù)同步至blog_lastest
- 刪除
blog
索引 - 數(shù)據(jù)同步后給
blog_lastest
添加別名blog
新建索引
在這里推薦一個(gè)ES管理工具Kibana,主要針對(duì)數(shù)據(jù)的探索衙猪、可視化和分析馍乙。
put /blog_lastest/
{
"mappings":{
"properties":{
"title":{
"type":"text",
"analyzer":"ik_max_word"
},
"author":{
"type":"keyword",
"fields":{
"seg":{
"type":"text",
"analyzer":"ik_max_word"
}
}
}
}
}
}
將舊索引數(shù)據(jù)copy到新索引
同步等待
接?將會(huì)在 reindex 結(jié)束后返回
POST /_reindex
{
"source": {
"index": "blog"
},
"dest": {
"index": "blog_lastest"
}
}
在 kibana
中的使用如下所示
當(dāng)然高版本(7.1.1)中,ES都有提供對(duì)應(yīng)的Java REST Client
垫释,比如
ReindexRequest reindexRequest = new ReindexRequest();
reindexRequest.setSourceIndices("blog").setSource.setDestIndex("blog_lastest");
TaskSubmissionResponse taskSubmissionResponse = client.submitReindexTask(reindexRequest, RequestOptions.DEFAULT);
為了防止贅述丝格,接下來(lái)舉例全部以kibana
中請(qǐng)求介紹,如果有需要用Java REST Client
棵譬,可以自行去ES官網(wǎng)查看显蝌。
異步執(zhí)?
如果 reindex 時(shí)間過?,建議加上 wait_for_completion=false
的參數(shù)條件茫船,這樣 reindex 將直接返回 taskId
琅束。
POST /_reindex?wait_for_completion=false
{
"source": {
"index": "blog"
},
"dest": {
"index": "blog_lastest"
}
}
返回:
{
"task" : "dpBihNSMQfSlboMGlTgCBA:4728038"
}
op_type 參數(shù)
op_type
參數(shù)控制著寫入數(shù)據(jù)的沖突處理方式,如果把 op_type
設(shè)置為 create
【默認(rèn)值】算谈,在 _reindex
API 中涩禀,表示寫入時(shí)只在 dest
index
中添加不存在的 doucment,如果相同的 document 已經(jīng)存在然眼,則會(huì)報(bào) version confilct
的錯(cuò)誤艾船,那么索引操作就會(huì)失敗「呙浚【這種方式與使用 _create API 時(shí)效果一致】
POST _reindex
{
"source": {
"index": "blog"
},
"dest": {
"index": "blog_lastest",
"op_type": "create"
}
}
如果這樣設(shè)置了屿岂,也就不存在更新數(shù)據(jù)的場(chǎng)景了【沖突數(shù)據(jù)無(wú)法寫入】,我們也可以把 op_type
設(shè)置為 index
鲸匿,表示所有的數(shù)據(jù)全部重新索引創(chuàng)建爷怀。
conflicts 配置
默認(rèn)情況下,當(dāng)發(fā)生 version conflict
的時(shí)候带欢,_reindex
會(huì)被 abort
运授,任務(wù)終止【此時(shí)數(shù)據(jù)還沒有 reindex
完成】,在返回體中的 failures
指標(biāo)中會(huì)包含沖突的數(shù)據(jù)【有時(shí)候數(shù)據(jù)會(huì)非常多】渡贾,除非把 conflicts
設(shè)置為 proceed
逗宜。
關(guān)于 abort
的說(shuō)明,如果產(chǎn)生了 abort
擂仍,已經(jīng)執(zhí)行的數(shù)據(jù)【例如更新寫入的】仍然存在于目標(biāo)索引,此時(shí)任務(wù)終止,還會(huì)有數(shù)據(jù)沒有被執(zhí)行亿昏,也就是漏數(shù)了。換句話說(shuō),該執(zhí)行過程不會(huì)回滾脊髓,只會(huì)終止。如果設(shè)置了 proceed
,任務(wù)在檢測(cè)到數(shù)據(jù)沖突的情況下律罢,不會(huì)終止,會(huì)跳過沖突數(shù)據(jù)繼續(xù)執(zhí)行悦冀,直到所有數(shù)據(jù)執(zhí)行完成师骗,此時(shí)不會(huì)漏掉正常的數(shù)據(jù),只會(huì)漏掉有沖突的數(shù)據(jù)。
POST _reindex
{
"source": {
"index": "blog"
},
"dest": {
"index": "blog_lastest",
"op_type": "create"
},
"conflicts": "proceed"
}
我們可以故意把 op_type
設(shè)置為 create
厂置,人為制造數(shù)據(jù)沖突的場(chǎng)景野揪,測(cè)試時(shí)更容易觀察到?jīng)_突現(xiàn)象。
如果把 conflicts
設(shè)置為 proceed
,在返回體結(jié)果中不會(huì)再出現(xiàn) failures
的信息,但是通過 version_conflicts
指標(biāo)可以看到具體的數(shù)量。
批次大小配置
當(dāng)你發(fā)現(xiàn)reindex
的速度有些慢的時(shí)候,可以在 query
參數(shù)的同一層次【即 source
參數(shù)中】添加 size
參數(shù),表示 scroll size
的大小【會(huì)影響批次的次數(shù),進(jìn)而影響整體的速度】捧韵,如果不顯式設(shè)置磷瘤,默認(rèn)是一批 1000 條數(shù)據(jù),在一開始的簡(jiǎn)單示例中也看到了膀斋。
如下梭伐,設(shè)置 scroll size
為 5000:
POST /_reindex?wait_for_completion=false
{
"source": {
"index": "blog",
"size":5000
},
"dest": {
"index": "blog_lastest",
"op_type": "create"
},
"conflicts": "proceed"
}
測(cè)試后糊识,速度達(dá)到了 30 分鐘 500 萬(wàn)左右拌滋,明顯提升了很多。
根據(jù)taskId可以實(shí)時(shí)查看任務(wù)的執(zhí)行狀態(tài)
一般來(lái)說(shuō)沧竟,如果我們的 source index
很大【比如幾百萬(wàn)數(shù)據(jù)量】铸敏,則可能需要比較長(zhǎng)的時(shí)間來(lái)完成 _reindex
的工作,可能需要幾十分鐘悟泵。而在此期間不可能一直等待結(jié)果返回桩撮,可以去做其它事情敦第,如果中途需要查看進(jìn)度,可以通過 _tasks
API 進(jìn)行查看店量。
GET /_tasks/{taskId}
返回:
{
"completed" : false,
"task" : {
"node" : "dpBihNSMQfSlboMGlTgCBA",
"id" : 4704218,
"type" : "transport",
"action" : "indices:data/write/reindex",
……
}
當(dāng)執(zhí)行完畢時(shí),completed
為true
查看任務(wù)進(jìn)度以及取消任務(wù)鞠呈,除了根據(jù)taskId查看以外融师,我們還可以通過查看所有的任務(wù)中篩選本次reindex
的任務(wù)。
GET _tasks?detailed=true&actions=*reindex
返回結(jié)果:
{
"nodes" : {
"dpBihNSMQfSlboMGlTgCBA" : {
"name" : "node-16111-9210",
"transport_address" : "192.168.XXX.XXX:9310",
"host" : "192.168.XXX.XXX",
"ip" : "192.168.16.111:9310",
"roles" : [
"ingest",
"master"
],
"attributes" : {
"xpack.installed" : "true",
"transform.node" : "false"
},
"tasks" : {
"dpBihNSMQfSlboMGlTgCBA:6629305" : {
"node" : "dpBihNSMQfSlboMGlTgCBA",
"id" : 6629305,
"type" : "transport",
"action" : "indices:data/write/reindex",
"status" : {
"total" : 8361421,
"updated" : 0,
"created" : 254006,
"deleted" : 0,
"batches" : 743,
"version_conflicts" : 3455994,
"noops" : 0,
"retries" : {
"bulk" : 0,
"search" : 0
},
"throttled_millis" : 0,
"requests_per_second" : -1.0,
"throttled_until_millis" : 0
},
"description" : "reindex from [blog] to [blog_lastest][_doc]",
"start_time_in_millis" : 1609338953464,
"running_time_in_nanos" : 1276738396689,
"cancellable" : true,
"headers" : { }
}
}
}
}
}
注意觀察里面的幾個(gè)重要指標(biāo)蚁吝,例如從 description
中可以看到任務(wù)描述旱爆,從 tasks 中可以找到任務(wù)的 id
【例如 dpBihNSMQfSlboMGlTgCBA:6629305
】,從 cancellable
可以判斷任務(wù)是否支持取消操作窘茁。
這個(gè) API 其實(shí)就是模糊匹配怀伦,同理也可以查詢其它類型的任務(wù)信息,例如使用 GET _tasks?detailed=true&actions=*byquery
查看查詢請(qǐng)求的狀態(tài)山林。
當(dāng)集群的任務(wù)太多時(shí)我們就可以根據(jù)task_id
房待,也就是上面提到GET /_tasks/task_id
方式更加準(zhǔn)確地查詢指定任務(wù)的狀態(tài),避免集群的任務(wù)過多驼抹,不方便查看桑孩。
如果遇到操作失誤的場(chǎng)景,想取消任務(wù)框冀,有沒有辦法呢流椒?
當(dāng)然有啦,雖然覆水難收明也,通過調(diào)用
_tasks API
:
POST _tasks/task_id/_cancel
這里的 task_id
就是通過上面的查詢?nèi)蝿?wù)接口獲取的任務(wù)id(任務(wù)要支持取消操作宣虾,即【cancellable 為 true】時(shí)方能收效)。
刪除舊索引
當(dāng)我們通過 API 查詢發(fā)現(xiàn)任務(wù)完成后温数,就可以進(jìn)行后續(xù)操作绣硝,我這里是要?jiǎng)h除舊索引,然后再給新索引起別名帆吻,用于替換舊索引域那,這樣才能保證對(duì)外服務(wù)沒有任何感知。
DELETE /blog
使用別名
POST /_aliases
{
"actions":[
{
"add":{
"index":"blog_lastest",
"alias":"blog"
}
}
]
}
通過別名訪問新索引
進(jìn)行過以上操作后猜煮,我們可以使用一個(gè)簡(jiǎn)單的搜索驗(yàn)證服務(wù)次员。
POST /blog/_search
{
"query": {
"match": {
"author": "james"
}
}
}
如果搜索結(jié)果達(dá)到我們的預(yù)期目標(biāo),至此王带,數(shù)據(jù)索引重建遷移完成淑蔚。
本文可轉(zhuǎn)載,但需聲明原文出處愕撰。 程序員小明刹衫,一個(gè)很少加班的程序員醋寝。歡迎關(guān)注微信公眾號(hào)“程序員小明”,獲取更多優(yōu)質(zhì)文章带迟。