您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
Elasticsearch Mapping类型修改
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
Elasticsearch Mapping类型修改
sc****
2023-08-22
IP归属:北京
5280浏览
## 背景 通常数据库进行分库分表后,目前比较常规的作法,是通过将数据异构到Elasticsearch来提供分页列表查询服务;在创建Elasticsearch索引时,基本都是会参考目前的业务需求、关系数据库中的类型以及对数据的相关规划来定义相关字段mapping的类型. 在Elasticsearch的mapping中的列(或则叫属性),有几个比较重要的参数([更多参数参考官方文档](https://www.elastic.co/guide/en/elasticsearch/reference/7.5/mapping.html)) - 列类型:`type` 指定了该列的数据类型,常用的有`text`, `keyword`, `date`, `long`, `double`,`boolean`以及 `object`和`nested`,不同的类型也有对应的不同查询方式,创建之后是不能修改的; - 是否可索引:`index` 该`index`选项控制字段值是否被索引。它接受`true` or `false`,并且默认为`true`. 未索引的字段不可查询,当然也不能做为排序字段。 但是在实际的开发过程中,又会有需求对现有的mapping的type进行修改(类似对MySQL数据表的字段进行DDL操作)的诉求。比如商品上的价格`price`字段,按原来的业务分析,只需要提供数据返回即可,在创建索引时类型定义了`keyword`了,并且`index`设置成了`false`,这时我们需要根据价格的范围查询或则进行排序操作,就希望对mapping进行调整,将类型修改成数字类型,索引也需要加上;今天针对Elasticsearch的Mapping类型进行修改,讨论几个可行的方案 ## 方案1:运用reindex 遇到问题第一时间,我们应该是查询官方文档是否有相关的操作说明,在官方文档中,确实还能找到对已有mapping更新的相关api [put-mapping](https://www.elastic.co/guide/en/elasticsearch/reference/7.5/indices-put-mapping.html),通过这个文档,很快可以找到文档中对修改已有mapping的列的方式([参考官方文档](https://www.elastic.co/guide/en/elasticsearch/reference/7.5/indices-put-mapping.html#updating-field-mappings)),同时也提到的通过 `reindex`的方式来修改已有类型的方式; > 除了支持的[mapping parameters](https://www.elastic.co/guide/en/elasticsearch/reference/7.5/mapping-params.html)外,您不能更改现有字段的映射或字段类型。更改现有字段可能会使已编制索引的数据无效。如果您需要更改字段的映射,请使用正确的映射创建一个新索引并将您的数据重新索引[reindex](https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-reindex.html)到该索引中。 如原来索引的mapping如下 ```json PUT /users { "mappings" : { "properties": { "user_id": { "type": "long" } } } } //加一了两条数据 POST /users/_doc?refresh=wait_for { "user_id" : 12345 } POST /users/_doc?refresh=wait_for { "user_id" : 12346 } ``` 这时想修改`user_id`的类型为`keyword`,我们直接是修改不了的。 ```json //尝试直接修改type,行不通,会报错 PUT /users/_mapping { "properties": { "user_id": { "type": "keyword" } } } //报错信息 { "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 } ``` 按官方文档说的`reindex`重新索引可按以下步骤操作 ### 操作步骤 #### 第一步:创建新的索引 `new_users` 将`user_id`的类型定义成 `keyword` ```json PUT /new_users { "mappings" : { "properties": { "user_id": { "type": "keyword" } } } } ``` #### 第二步:将原`user`索引标记为只读 控制我们的应用系统,数据停写不再向老索引中写数据,并且最好对老索引进行只读操作设置,保证在reindex的过程中,不要生产新数据,导致新老索数据不一致; ```json //设置索引为读写的 PUT /users/_settings { "settings": { "index.blocks.write": true } } ``` #### 第三步:将原`user`索引中的数据迁移到`new_users`中 ```json POST /_reindex { "source": { "index": "users" }, "dest": { "index": "new_users" } } ``` `reindex`还有很多的参数可以配置,包括从远程的一个集群迁移数据都是可以的,详细可参考:[Reindex API](https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-reindex.html) 如果新的索引的mapping的定义与原索引的定义有差异的,会按新索引定义的 `dynamic` 规则进行数据的迁移,具体的,可以参考: [dynamic](https://www.elastic.co/guide/en/elasticsearch/reference/7.5/dynamic.html) 该`dynamic`设置控制是否可以动态添加新字段。它接受三种设置: |值|说明| |---|---| |true|新检测到的字段被添加到映射中。(`默认`); 新增的数据类型的规则,可以参考:[dynamic-mapping](https://www.elastic.co/guide/en/elasticsearch/reference/7.5/dynamic-mapping.html)| |false|忽略新检测到的字段。这些字段不会被编入索引,因此将无法搜索,但仍会出现在`_source`返回的命中字段中。这些字段不会添加到映射中,必须明确添加新字段。| |strict|如果检测到新字段,则会抛出异常并拒绝文档。必须将新字段显式添加到映射中。| 同时将原`user`索引标记为可读写 ```json //设置索引为可读写 PUT /users/_settings { "settings": { "index.blocks.write": false } } ``` #### 第四步:切换到使用新的mapping 1. 可以将应用系统中的配置改成新索引 2. 也可以通过索引的别名的方式为新索引增加原来老索引的别名来操作,为索引增加别名参考文档:[Add index alias API](https://www.elastic.co/guide/en/elasticsearch/reference/7.5/indices-add-alias.html),在增加别名前,需要删除原来的老索引; ```json //为索引增加别名 基本格式 PUT /<index>/_alias/<alias> POST /<index>/_alias/<alias> //为new_users索引增加别名users PUT /new_users/_alias/users //没有删除老索引前,是增加不了别名的,需要先删除老别名 { "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 } ``` ### 方案优劣分析 #### 【优点】操作简单,官方方案 该方案,不需要对原索引做操作,在线即可进行,并且操作步骤也简单;也是官方文档提供的方案。 #### 【缺点】数据量大迁移耗时长 当数据最大时,这个数据迁移会比较耗时 ### 结论 当数据量小时,并且希望mapping比较规整好看,该方案是比较推荐的。当数据量大时,可能该方案在数据迁移过程中会比较耗时,需要评估是否可行; ## 方案2:运用multi-fields > 为不同的目的以不同的方式索引同一个字段通常很有用。这就是`multi-fields`的目的。例如,一个`string` 字段可以映射为text用于全文搜索的字段,也可以映射`keyword`为用于排序或聚合的字段; 在这个方案中,应用的是mapping参数`fields`来对同一个列,定义多种数据类型;详细[【官方文档】multi-fields] (https://www.elastic.co/guide/en/elasticsearch/reference/7.5/multi-fields.html) ### 操作步骤 #### 第一步:为列增加`fields`属性 还是以上面的`users`这个索引为例,我们还是想将`user_id`的类型定义成 `keyword`; ```json PUT /users/_mapping { "properties":{ "user_id":{ "type":"long", "fields":{ "raw":{ "type":"keyword" } } } } } ``` 操作完成后,在`users`的 `user_id`列下,就会多出一个`raw`的子属性;在我们正常写数据`user_id`时,会自动生成这两个索引,一个是`long`类型的`user_id`,以及`keyword`类型的`user_id.raw`(注意这里有个点,跟子对象访问方式一样); 在put mapping时,`type`参数必需给,并且需要跟原来的类型一致,`fields`中新定义的子属性可以多个; #### 【可选】第二步:历史数据更新 针对历史数据需要处理,可以借助`_update_by_query` 来更新数据,只需要将原来的索引再写一次,即可将新加的字段写入数据。 ```json POST /users/_update_by_query { "query":{ "exists":{ "field":"user_id" } }, "script":{ "source":"ctx._source.user_id=ctx._source.user_id ", "lang":"painless" } } // query 部分为需要更新数据过滤条件,可根据业务规则写 // script 更数据的逻辑,这个基本可以不改 ``` ### 方案优劣分析 #### 【优点】不影响原索引,同一列可以定义多种类型 通过这方式不会影响原来的索引数据,可以不用修改现在的应用程序的读写方式,对应用程序一切按原来逻辑执行,对应用方无感知,非常优化。只需要有使用新类型的场景使用即可,可以说影响是最小的; 同时只是做了一个定义,执行速度是非常快的,对Elasticsearch服务基本不会有太大影响;并且对于同一个列可以定义多个类型,比如商品名称,在多国多语言环境下可以根据不同语言定义多个列,对应使用不同的分词器; #### 【缺点】老数据不会自动创建子索引,多出额外的存储 老数据不会自动创建索引,因为需要多出新的索引来,会增加额外的存储; ### 结论 1、需要对多一列创建多个索引类型时,是一个非常推荐的方案; 2、对于新索引,只有新业务使用,对老数据没有诉求的,也非常推荐该方案; ## 方案3:运用copy_to `copy_to`是将多个字段的值,合并到一个字段中,便于搜索。但是也可以实现一个字段存在多个类型的需求。详细参考[【官方文档】copy_to](https://www.elastic.co/guide/en/elasticsearch/reference/7.5/copy-to.html) ### 操作步骤 还是用上面的`users`这个索引为例,为`user_id`创建一个copy列: `user_id_raw` 类型定义成 `keyword` ```json PUT /users/_mapping { "properties":{ "user_id_raw":{ "type":"keyword", "copy_to":"user_id" } } } ``` 这个方案与`方案2:multi-fields` 基本是一样的,只是创建列的方式不同,优缺点都一样; ## 参考资料 - [1] [【官方文档】Mapping parameters](https://www.elastic.co/guide/en/elasticsearch/reference/7.5/mapping-params.html) - [2] [【官方文档】Mapping Field datatypes](https://www.elastic.co/guide/en/elasticsearch/reference/7.5/mapping-types.html) - [3] [【官方文档】multi-fields] (https://www.elastic.co/guide/en/elasticsearch/reference/7.5/multi-fields.html) - [4] [Elasticsearch Rename Index](https://linuxhint.com/elasticsearch-rename-index/) - [5] [elasticSearch7.x—mapping中的fields属性||copy_to配置(同一个字段两种类型)](https://www.jianshu.com/p/320906e812af) - [6] [《Elasticsearch:权威指南》Mapping -- Mapping parameters -- fields(multi-fields)](https://blog.csdn.net/m0_45406092/article/details/107912022)
上一篇:K8S集群中使用JDOS KMS服务对敏感数据安全加密
下一篇:从头到尾说一次 Spring 事务管理(器)
sc****
文章数
1
阅读量
132
作者其他文章
01
Elasticsearch Mapping类型修改
背景通常数据库进行分库分表后,目前比较常规的作法,是通过将数据异构到Elasticsearch来提供分页列表查询服务;在创建Elasticsearch索引时,基本都是会参考目前的业务需求、关系数据库中的类型以及对数据的相关规划来定义相关字段mapping的类型. 在Elasticsearch的mapping中的列(或则叫属性),有几个比较重要的参数(更多参数参考官方文档)列类型:type指定了该
sc****
文章数
1
阅读量
132
作者其他文章
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号