背景
通常数据库进行分库分表后,目前比拟惯例的作法,是通过将数据异构到 Elasticsearch 来提供分页列表查问服务;在创立 Elasticsearch 索引时,根本都是会参考目前的业务需要、关系数据库中的类型以及对数据的相干布局来定义相干字段 mapping 的类型.
在 Elasticsearch 的 mapping 中的列(或则叫属性),有几个比拟重要的参数(更多参数参考官网文档)
-
列类型:
type
指定了该列的数据类型,罕用的有
text
,keyword
,date
,long
,double
,boolean
以及object
和nested
, 不同的类型也有对应的不同查问形式,创立之后是不能批改的; -
是否可索引:
index
该
index
选项管制字段值是否被索引。它承受true
orfalse
, 并且默认为true
. 未索引的字段不可查问, 当然也不能做为排序字段。
然而在理论的开发过程中,又会有需要对现有的 mapping 的 type 进行批改(相似对 MySQL 数据表的字段进行 DDL 操作)的诉求。比方商品上的价格 price
字段,按原来的业务剖析,只须要提供数据返回即可,在创立索引时类型定义了 keyword
了,并且 index
设置成了false
,这时咱们须要依据价格的范畴查问或则进行排序操作,就心愿对 mapping 进行调整,将类型批改成数字类型,索引也须要加上;明天针对 Elasticsearch 的 Mapping 类型进行批改,探讨几个可行的计划
计划 1:使用 reindex
遇到问题第一工夫,咱们应该是查问官网文档是否有相干的操作阐明,在官网文档中,的确还能找到对已有 mapping 更新的相干 apiput-mapping, 通过这个文档,很快能够找到文档中对批改已有 mapping 的列的形式(参考官网文档),同时也提到的通过 reindex
的形式来批改已有类型的形式;
除了反对的 mapping parameters 外,您不能更改现有字段的映射或字段类型。更改现有字段可能会使已编制索引的数据有效。如果您须要更改字段的映射,请应用正确的映射创立一个新索引并将您的数据从新索引 reindex 到该索引中。
如原来索引的 mapping 如下
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
, 咱们间接是批改不了的。
// 尝试间接批改 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
PUT /new_users
{
"mappings" : {
"properties": {
"user_id": {"type": "keyword"}
}
}
}
第二步:将原 user
索引标记为只读
管制咱们的利用零碎,数据停写不再向老索引中写数据,并且最好对老索引进行只读操作设置,保障在 reindex 的过程中,不要生产新数据,导致新老索数据不统一;
// 设置索引为读写的
PUT /users/_settings
{
"settings": {"index.blocks.write": true}
}
第三步:将原 user
索引中的数据迁徙到 new_users
中
POST /_reindex
{
"source": {"index": "users"},
"dest": {"index": "new_users"}
}
reindex
还有很多的参数能够配置,包含从近程的一个集群迁徙数据都是能够的,具体可参考:Reindex API
如果新的索引的 mapping 的定义与原索引的定义有差别的,会按新索引定义的 dynamic
规定进行数据的迁徙,具体的,能够参考:dynamic
该 dynamic
设置管制是否能够动静增加新字段。它承受三种设置:
值 | 阐明 |
---|---|
true | 新检测到的字段被增加到映射中。(默认 ); 新增的数据类型的规定,能够参考:dynamic-mapping |
false | 疏忽新检测到的字段。这些字段不会被编入索引,因而将无奈搜寻,但仍会呈现在 _source 返回的命中字段中。这些字段不会增加到映射中,必须明确增加新字段。 |
strict | 如果检测到新字段,则会抛出异样并回绝文档。必须将新字段显式增加到映射中。 |
同时将原 user
索引标记为可读写
// 设置索引为可读写
PUT /users/_settings
{
"settings": {"index.blocks.write": false}
}
第四步:切换到应用新的 mapping
- 能够将利用零碎中的配置改成新索引
- 也能够通过索引的别名的形式为新索引减少原来老索引的别名来操作,为索引减少别名参考文档:Add index alias API,在减少别名前,须要删除原来的老索引;
// 为索引减少别名 根本格局
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
;
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
来更新数据,只须要将原来的索引再写一次,即可将新加的字段写入数据。
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
操作步骤
还是用下面的 users
这个索引为例,为 user_id
创立一个 copy 列:user_id_raw
类型定义成keyword
PUT /users/_mapping
{
"properties":{
"user_id_raw":{
"type":"keyword",
"copy_to":"user_id"
}
}
}
这个计划与 计划 2:multi-fields
根本是一样的,只是创立列的形式不同,优缺点都一样;
参考资料
- [1]【官网文档】Mapping parameters
- [2]【官网文档】Mapping Field datatypes
- [3] [【官网文档】multi-fields] (https://www.elastic.co/guide/en/elasticsearch/reference/7.5/multi-fields.html)
- [4]Elasticsearch Rename Index
- [5]elasticSearch7.x—mapping 中的 fields 属性 ||copy_to 配置(同一个字段两种类型)
- [6]《Elasticsearch: 权威指南》Mapping — Mapping parameters — fields(multi-fields)
作者:京东批发 周德东
起源:京东云开发者社区 转载请注明起源