Elasticsearch数据建模之——mapping字段的相关配置
es作为以倒排索引为基础实现的存储体系,不遵循关系型数据库中的范式约定,它的数据建模,是根据部署需求和性能需求的物理模型;而在这个建模的过程中,由于es是可以水平扩展的,可以存储到TB级以上的大数据的原因,他的一些预配置就显得尤其重要了,这关系到整个es的性能和查询效率。
—//—
类似于关系型数据库里面的表结构设计,es中对应的是mapping配置;这一章主要讲的就是mapping设置的时候的一些注意点。
mapping字段 的相关配置
名称 | 枚举 | 作用 |
---|---|---|
enable |
true/false | 仅存储,不做搜索和聚合分析 |
index |
true/false | 是否构建倒排索引,false不记录,即不可搜索 |
index_options |
docss/freqs/positions/offsets | 存储倒排索引的哪些信息,text类型默认配置为positions,其他默认为docs ,记录内容越多,占用空间越大。 |
norms |
true/false | 是否存储归一化的相关参数,如果字段仅用于过滤和聚合分析,可关闭 |
doc_values |
true/false | 是否开启,用于排序和聚合分析 |
field_data |
true/false | 是否为text类型启用,实现排序和聚合分析 |
store |
true/false | 是否存储该字段值 |
coerce |
true/false | 是否开启自动数据类型转换,比如字符串转数字,浮点转int(默认是true) |
multifields |
多字段:灵活使用多字段来解决多样的业务需求 | |
dynamic |
true/false/strict | 控制mapping的自动跟新 |
date_detection |
true/false | 是否自动识别日期类型 (建议false,手动去设置格式) |
Mapping字段属性的设定流程
判断是何种类型的字段
- 字符串类型:需要分词就设定为text,否则设定为keyword类型
- 枚举值类型:基于性能考虑设定成keyword类型,即便该数据为整型(比如状态码)
- 数值类型:尽量选择贴近的数据类型,比如byte即可表示所有数值的时候,选择byte,不要用long
- 其他类型:比如布尔类型,日期,地理数据等
判断该字段是否需要检索
- 完全不需要检索,排序,聚合分析的字段:
enable设置为false
- 不需要检索的字段:
index设置为false
- 需要检索的字段,可以根据如下的配置设定需要的存储力度:
-
index_options
: 结合需要设定 -
norms
: 不需要归一化数据时关闭即可
-
- 是否需要排序和聚合分析?
不需要排序或者聚合分析功能:doc_values
设定为false;fielddata
设定为false -
是否需要专门存储当前字段的数据?
store
设定为true,即可存储该字段的原始内容(与_source中的不相关) 一般结合_source的enabled
设定为false时使用其他建议
1.对mapping进行版本管理
包含在代码或者以专门的文件进行管理,添加好注释,并加入git等版本管理仓库中方便回顾
为每个增加一个metadata字段,在其中维护一些文档相关的元数据,方便对数据进行管理
{
"metadata":{
"version":1
},
"username":"alfred",
"job":"engineer"
}
#mapping版本,可以自行制定,比如每次更新mapping设置后,该version加1
2.防止字段过多
字段过多:难于维护,当字段成百上千时,基本很难有人明确知道每个字段的含义;mapping的信息存储在cluster state里面,过多的字段会导致mapping过大,最终导致更新变慢。
通过设置index.mapping.total_fields.limit
可以限定索引中最大字段数,默认为1000
可以通过key/value的方式解决字段过多的问题,但并不完美(实现方式这里就不赘述了):
key/value方式缺点:
query语句复杂度飙升,且有一些可能无法实现,比如聚合分析相关的
不利于在kibana中做可视化分析
一般字段过多的原因是由于没有高质量的数据建模导致的,比如dynamic
设置为true,考虑拆分多个索引来解决问题。
实例
以博客文章blog_index
为例说明创建过程:
字段 | 字段名称 | 字段类型 |
---|---|---|
标题 | title | text,子字段keyword |
发布日期 | publish_date | date |
作者 | author | keyword |
摘要 | abstract | text |
网络地址 | url | keyword(需要做相应修改) |
内容 | content | ??? |
思考:博客的
content
内容可能会有几百上千字,但是如果博客换成书,content
的内容可能会有几十万字,变的非常的大。这样如果content
字段依然默认为text
类型:在取原始数据时,是通过_source
来获取原始内容的,每一次取_source
都会把content
内容取出来,如果content
过大就会导致_source
获取过多的内容,降低性能。
mapping
设置成如下内容:结合_source
的enabled
设定为false
时设置store
为true
PUT blog_index
{
"mappings": {
"doc":{
"_source": {
"enabled": false
},
"properties": {
"title":{
"type":"text",
"fields": {
"keyword":{
"type": "keyword"
}
},
"store": true
},
"publish_date":{
"type": "date",
"store": true
},
"author":{
"type": "keyword",
"store": true
},
"abstract":{
"type": "text",
"store": true
},
"aontent":{
"type":"text",
"store": true
},
"url":{
"type": "keyword",
"doc_values":false,
"norms": false,
"ignore_above": 100,
"store": true
}
}
}
}
}
配置数据对比:
查询实例
查询高亮显示:
GET blog_index/_search
{
"stored_fields": ["title","publish_date","author","abstract","url"], #不获取content字段
"query": {
"match": {
"content": "blog"
}
},
"highlight": {
"fields": {"content": {}}
}
}
关于mapping的修改问题
mapping一旦建立,就不能更改现有的字段,就不能更改现有的字段映射,如果要推到现有的映射,需要建立一个新的索引,然后重新定义映射然后把之前索引里的数据导入到新建的索引里。
实现方式有如下几种:
一、使用别名的方法
-
1.给现有的索引定义一个别名,并且把现有的索引指向这个别名。运行:
PUT /现有索引/_alias/别名索引
- 2.新创建一个索引,定义好最新的映射
-
3.将别名指向新的索引,并且取消之前索引的指向。运行:
POST /_aliases { "actions": [ { "remove": { "index": "现有索引名", "alias": "别名A" } } { "add": { "index": "新建索引名", "alias": "别名A" } } ] }
以上步骤可以实现索引的平滑过渡,并且是零停机的。
二、使用reindex
使用reindex
来重建索引,这个的过程是使用的快照的办法,所以在重新索引的时候,一定要停止往里面添加索引的时候去操作,不然快照无法统计到哪个时候的信息,写法如下:
POST _reindex
{
"source": {
"index": "log-yht-tbs-test-1-2018.09.06"
},
"dest": {
"index": "log_yht_tbs_2018.09.06_back"
}
}
数据重建的时间收文档规模的影响,规模越大,所需时间越久,此时通过设定url的参数
wait_for_completion
为false
来异步执行,ES以task来描述此类执行任务,
es提供了api来查看任务进度和相关数据。
三、_update_by_query
_update_by_query
是指在现有索引上重建,和上面两种都不一样。
Mapping设置补充
当字段遇到null值时的处理策略
null_value
: 当字段遇到null值时的处理策略,默认为null,即空值,此时es会忽略该值。可以通过设定该值设定字段的默认值
PUT myindex1
{
"mappings": {
"doc": {
"properties": {
"status_code": {
"type": "keyword",
"null_value": "NULL"
}
}
}
}
}
multi-fields多字段特性
允许对同一个字段采用不同的配置,比如分词,常见的例子如对人名实现拼音搜索,只需要在人名中新增一个子字段为pinyin
即可。
PUT myindex1
{
"mappings": {
"doc": {
"properties": {
"username": {
"type":"text",
"fields": {
"pinyin": {
"type": "text",
"analyzer": "pinyin"
}
}
}
}
}
}
}
GET myindex1/_search
{
"query": {
"match": {
"username.pinyin": "hanhan"
}
}
}
动态字段映射
es可以自动识别文档字段类型,从而降低用户使用成本;
es是依靠JSOn文档的字段类型来实现自动识别字段类型,支持的类型如下:
json类型 | es类型 |
---|---|
null | 忽略 |
Boolean | Boolean |
浮点类型 | float |
整数 | long |
object | object |
array | 由第一个非null值的类型决定 |
string | 匹配为日期则设为date类型(默认开启,建议关闭,自己配置format);匹配为数字的话设为float或long类型(默认关闭);设为text类型,并且附带keyword的子字段 |
其中,我们讲下日期识别:
默认是[ "strict_date_optional_time","yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z"]
是ISO datetime的格式,完整格式类似为:
"strict_date_optional_time"YYYY-MM-DDThh:mm:ssTZD(eg 1997-07-16T19:20:30+01:00)
-
dynamic_date_formats
可以自定义日期类型 -
date_detection
可以关闭日期自动识别的机制
PUT my_index
{
"mappings": {
"my_type":{
"dynamic_date_formats": ["yyyy-MM-dd"]
}
}
}
动态模板
允许根据es自动识别的数据类型、字段名等动态设定字段类型,可以实现如下效果:
- 所有字符串都设定为
keyword
类型,即默认不分词 - 所有以
message
开头字段都设定为text类型,即分词 - 所有以
long_
开头的字段都设定为long类型 - 所有自动匹配为
double
类型的都设定为float
类型,以节省空间
PUT test_index
{
"mappings": {
"doc":{
"dynamic_templates": [{ #数组,可以指定多个匹配规则
"strings":{ #template的名称
"match_mapping_type":"string", #匹配规则
"mapping":{
"type":"keyword" #设置mapping信息
}
}
}]
}
}
}
匹配规则一般有如下几个参数:
-
match_mapping_type
: 匹配es自动识别的字段类型,如boolean,long,string等 -
match,unmatch
:匹配字段名 -
path_match,path_unmatch
: 匹配路径
案例:
(一)设置字符串默认使用keyword类型
PUT test_index
{
"mappings": {
"doc": {
"dynamic_templates": [
{
"strings_as_keywords": {
"match_mapping_type": "string",
"mapping":{
"type": "keyword"
}
}
}
]
}
}
}
PUT test_index/doc/1
{
"name": "alfred"
}
GET test_index/_mapping
设置以message开头的字段都设置为text类型 (顺序由上而下)
PUT test_index
{
"mappings": {
"doc": {
"dynamic_templates": [
{
"message_as_text": {
"match_mapping_type": "string",
"match": "message*",
"mapping":{
"type": "text"
}
}
},
{
"strings_as_keywords": {
"match_mapping_type": "string",
"mapping":{
"type": "keyword"
}
}
}
]
}
}
}
索引模板
索引模板:英文为 Index Template
,主要用于在新建索引时自动应用预先设定的配置,简化索引创建的操作步骤。
- 可以设定索引的配置和mapping
- 可以有多个模板,根据order设置,order大的覆盖小的配置
索引模板API,endpoint为_template,如下所示: