通常數(shù)據(jù)庫(kù)進(jìn)行分庫(kù)分表后,目前比較常規(guī)的作法,是通過(guò)將數(shù)據(jù)異構(gòu)到Elasticsearch來(lái)提供分頁(yè)列表查詢(xún)服務(wù);在創(chuàng)建Elasticsearch索引時(shí),基本都是會(huì)參考目前的業(yè)務(wù)需求、關(guān)系數(shù)據(jù)庫(kù)中的類(lèi)型以及對(duì)數(shù)據(jù)的相關(guān)規(guī)劃來(lái)定義相關(guān)字段mapping的類(lèi)型.在Elasticsearch的mapping中的列(或則叫屬性),有幾個(gè)比較重要的參數(shù)(更多參數(shù)參考官方文檔)
列類(lèi)型:type
【資料圖】
指定了該列的數(shù)據(jù)類(lèi)型,常用的有text
,keyword
,date
,long
,double
,boolean
以及object
和nested
,不同的類(lèi)型也有對(duì)應(yīng)的不同查詢(xún)方式,創(chuàng)建之后是不能修改的;
是否可索引:index
該index
選項(xiàng)控制字段值是否被索引。它接受true
orfalse
,并且默認(rèn)為true
. 未索引的字段不可查詢(xún),當(dāng)然也不能做為排序字段。
但是在實(shí)際的開(kāi)發(fā)過(guò)程中,又會(huì)有需求對(duì)現(xiàn)有的mapping的type進(jìn)行修改(類(lèi)似對(duì)MySQL數(shù)據(jù)表的字段進(jìn)行DDL操作)的訴求。比如商品上的價(jià)格price
字段,按原來(lái)的業(yè)務(wù)分析,只需要提供數(shù)據(jù)返回即可,在創(chuàng)建索引時(shí)類(lèi)型定義了keyword
了,并且index
設(shè)置成了false
,這時(shí)我們需要根據(jù)價(jià)格的范圍查詢(xún)或則進(jìn)行排序操作,就希望對(duì)mapping進(jìn)行調(diào)整,將類(lèi)型修改成數(shù)字類(lèi)型,索引也需要加上;今天針對(duì)Elasticsearch的Mapping類(lèi)型進(jìn)行修改,討論幾個(gè)可行的方案
遇到問(wèn)題第一時(shí)間,我們應(yīng)該是查詢(xún)官方文檔是否有相關(guān)的操作說(shuō)明,在官方文檔中,確實(shí)還能找到對(duì)已有mapping更新的相關(guān)apiput-mapping,通過(guò)這個(gè)文檔,很快可以找到文檔中對(duì)修改已有mapping的列的方式(參考官方文檔),同時(shí)也提到的通過(guò)reindex
的方式來(lái)修改已有類(lèi)型的方式;
除了支持的mapping parameters外,您不能更改現(xiàn)有字段的映射或字段類(lèi)型。更改現(xiàn)有字段可能會(huì)使已編制索引的數(shù)據(jù)無(wú)效。如果您需要更改字段的映射,請(qǐng)使用正確的映射創(chuàng)建一個(gè)新索引并將您的數(shù)據(jù)重新索引reindex到該索引中。
如原來(lái)索引的mapping如下
PUT /users{ "mappings" : { "properties": { "user_id": { "type": "long" } } }}//加一了兩條數(shù)據(jù)POST /users/_doc?refresh=wait_for{ "user_id" : 12345}POST /users/_doc?refresh=wait_for{ "user_id" : 12346}
這時(shí)想修改user_id
的類(lèi)型為keyword
,我們直接是修改不了的。
//嘗試直接修改type,行不通,會(huì)報(bào)錯(cuò)PUT /users/_mapping{ "properties": { "user_id": { "type": "keyword" } }}//報(bào)錯(cuò)信息{ "error": { "root_cause": [ { "type": "illegal_argument_exception", "reason": "mapper [user_id] of different type, current_type [long], merged_type [keyword]" } ], "type": "illegal_argument_exception", "reason": "mapper [user_id] of different type, current_type [long], merged_type [keyword]" }, "status": 400}
按官方文檔說(shuō)的reindex
重新索引可按以下步驟操作
new_users
將user_id
的類(lèi)型定義成keyword
PUT /new_users{ "mappings" : { "properties": { "user_id": { "type": "keyword" } } }}
第二步:將原user
索引標(biāo)記為只讀控制我們的應(yīng)用系統(tǒng),數(shù)據(jù)停寫(xiě)不再向老索引中寫(xiě)數(shù)據(jù),并且最好對(duì)老索引進(jìn)行只讀操作設(shè)置,保證在reindex的過(guò)程中,不要生產(chǎn)新數(shù)據(jù),導(dǎo)致新老索數(shù)據(jù)不一致;
//設(shè)置索引為讀寫(xiě)的PUT /users/_settings{ "settings": { "index.blocks.write": true }}
第三步:將原user
索引中的數(shù)據(jù)遷移到new_users
中POST /_reindex{ "source": { "index": "users" }, "dest": { "index": "new_users" }}
reindex
還有很多的參數(shù)可以配置,包括從遠(yuǎn)程的一個(gè)集群遷移數(shù)據(jù)都是可以的,詳細(xì)可參考:Reindex API
如果新的索引的mapping的定義與原索引的定義有差異的,會(huì)按新索引定義的dynamic
規(guī)則進(jìn)行數(shù)據(jù)的遷移,具體的,可以參考:dynamic
該dynamic
設(shè)置控制是否可以動(dòng)態(tài)添加新字段。它接受三種設(shè)置:
值 | 說(shuō)明 |
---|---|
true | 新檢測(cè)到的字段被添加到映射中。(默認(rèn) ); 新增的數(shù)據(jù)類(lèi)型的規(guī)則,可以參考:dynamic-mapping |
false | 忽略新檢測(cè)到的字段。這些字段不會(huì)被編入索引,因此將無(wú)法搜索,但仍會(huì)出現(xiàn)在_source 返回的命中字段中。這些字段不會(huì)添加到映射中,必須明確添加新字段。 |
strict | 如果檢測(cè)到新字段,則會(huì)拋出異常并拒絕文檔。必須將新字段顯式添加到映射中。 |
同時(shí)將原user
索引標(biāo)記為可讀寫(xiě)
//設(shè)置索引為可讀寫(xiě)PUT /users/_settings{ "settings": { "index.blocks.write": false }}
第四步:切換到使用新的mapping可以將應(yīng)用系統(tǒng)中的配置改成新索引也可以通過(guò)索引的別名的方式為新索引增加原來(lái)老索引的別名來(lái)操作,為索引增加別名參考文檔:Add index alias API,在增加別名前,需要?jiǎng)h除原來(lái)的老索引;//為索引增加別名 基本格式PUT //_alias/POST //_alias///為new_users索引增加別名usersPUT /new_users/_alias/users//沒(méi)有刪除老索引前,是增加不了別名的,需要先刪除老別名{ "error": { "root_cause": [ { "type": "invalid_alias_name_exception", "reason": "Invalid alias name [users], an index exists with the same name as the alias", "index_uuid": "8Rbq_32BTHC4CoO_CqWdXA", "index": "users" } ], "type": "invalid_alias_name_exception", "reason": "Invalid alias name [users], an index exists with the same name as the alias", "index_uuid": "8Rbq_32BTHC4CoO_CqWdXA", "index": "users" }, "status": 400}
方案優(yōu)劣分析【優(yōu)點(diǎn)】操作簡(jiǎn)單,官方方案該方案,不需要對(duì)原索引做操作,在線即可進(jìn)行,并且操作步驟也簡(jiǎn)單;也是官方文檔提供的方案。
【缺點(diǎn)】數(shù)據(jù)量大遷移耗時(shí)長(zhǎng)當(dāng)數(shù)據(jù)最大時(shí),這個(gè)數(shù)據(jù)遷移會(huì)比較耗時(shí)
結(jié)論當(dāng)數(shù)據(jù)量小時(shí),并且希望mapping比較規(guī)整好看,該方案是比較推薦的。當(dāng)數(shù)據(jù)量大時(shí),可能該方案在數(shù)據(jù)遷移過(guò)程中會(huì)比較耗時(shí),需要評(píng)估是否可行;
方案2:運(yùn)用multi-fields操作步驟第一步:為列增加為不同的目的以不同的方式索引同一個(gè)字段通常很有用。這就是
multi-fields
的目的。例如,一個(gè)string
字段可以映射為text用于全文搜索的字段,也可以映射keyword
為用于排序或聚合的字段;在這個(gè)方案中,應(yīng)用的是mapping參數(shù)fields
來(lái)對(duì)同一個(gè)列,定義多種數(shù)據(jù)類(lèi)型;詳細(xì)[【官方文檔】multi-fields] (https://www.elastic.co/guide/en/elasticsearch/reference/7.5/multi-fields.html)
fields
屬性還是以上面的users
這個(gè)索引為例,我們還是想將user_id
的類(lèi)型定義成keyword
;
PUT /users/_mapping{ "properties":{ "user_id":{ "type":"long", "fields":{ "raw":{ "type":"keyword" } } } }}
操作完成后,在users
的user_id
列下,就會(huì)多出一個(gè)raw
的子屬性;在我們正常寫(xiě)數(shù)據(jù)user_id
時(shí),會(huì)自動(dòng)生成這兩個(gè)索引,一個(gè)是long
類(lèi)型的user_id
,以及keyword
類(lèi)型的user_id.raw
(注意這里有個(gè)點(diǎn),跟子對(duì)象訪問(wèn)方式一樣);在put mapping時(shí),type
參數(shù)必需給,并且需要跟原來(lái)的類(lèi)型一致,fields
中新定義的子屬性可以多個(gè);
針對(duì)歷史數(shù)據(jù)需要處理,可以借助_update_by_query
來(lái)更新數(shù)據(jù),只需要將原來(lái)的索引再寫(xiě)一次,即可將新加的字段寫(xiě)入數(shù)據(jù)。
POST /users/_update_by_query { "query":{ "exists":{ "field":"user_id" } }, "script":{ "source":"ctx._source.user_id=ctx._source.user_id ", "lang":"painless" }}// query 部分為需要更新數(shù)據(jù)過(guò)濾條件,可根據(jù)業(yè)務(wù)規(guī)則寫(xiě)// script 更數(shù)據(jù)的邏輯,這個(gè)基本可以不改
方案優(yōu)劣分析【優(yōu)點(diǎn)】不影響原索引,同一列可以定義多種類(lèi)型通過(guò)這方式不會(huì)影響原來(lái)的索引數(shù)據(jù),可以不用修改現(xiàn)在的應(yīng)用程序的讀寫(xiě)方式,對(duì)應(yīng)用程序一切按原來(lái)邏輯執(zhí)行,對(duì)應(yīng)用方無(wú)感知,非常優(yōu)化。只需要有使用新類(lèi)型的場(chǎng)景使用即可,可以說(shuō)影響是最小的;同時(shí)只是做了一個(gè)定義,執(zhí)行速度是非??斓?,對(duì)Elasticsearch服務(wù)基本不會(huì)有太大影響;并且對(duì)于同一個(gè)列可以定義多個(gè)類(lèi)型,比如商品名稱(chēng),在多國(guó)多語(yǔ)言環(huán)境下可以根據(jù)不同語(yǔ)言定義多個(gè)列,對(duì)應(yīng)使用不同的分詞器;
【缺點(diǎn)】老數(shù)據(jù)不會(huì)自動(dòng)創(chuàng)建子索引,多出額外的存儲(chǔ)老數(shù)據(jù)不會(huì)自動(dòng)創(chuàng)建索引,因?yàn)樾枰喑鲂碌乃饕齺?lái),會(huì)增加額外的存儲(chǔ);
結(jié)論1、需要對(duì)多一列創(chuàng)建多個(gè)索引類(lèi)型時(shí),是一個(gè)非常推薦的方案;2、對(duì)于新索引,只有新業(yè)務(wù)使用,對(duì)老數(shù)據(jù)沒(méi)有訴求的,也非常推薦該方案;
方案3:運(yùn)用copy_tocopy_to
是將多個(gè)字段的值,合并到一個(gè)字段中,便于搜索。但是也可以實(shí)現(xiàn)一個(gè)字段存在多個(gè)類(lèi)型的需求。詳細(xì)參考【官方文檔】copy_to
還是用上面的users
這個(gè)索引為例,為user_id
創(chuàng)建一個(gè)copy列:user_id_raw
類(lèi)型定義成keyword
PUT /users/_mapping{ "properties":{ "user_id_raw":{ "type":"keyword", "copy_to":"user_id" } }}
這個(gè)方案與方案2:multi-fields
基本是一樣的,只是創(chuàng)建列的方式不同,優(yōu)缺點(diǎn)都一樣;
作者:京東零售 周德東
來(lái)源:京東云開(kāi)發(fā)者社區(qū) 轉(zhuǎn)載請(qǐng)注明來(lái)源
關(guān)鍵詞: