欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Elasticsearch数据建模之——mapping字段的相关配置

程序员文章站 2022-03-15 17:07:55
...

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设置成如下内容:结合_sourceenabled设定为false时设置storetrue

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          
        }        
      }
    }
  }
}           

配置数据对比:
Elasticsearch数据建模之——mapping字段的相关配置
Elasticsearch数据建模之——mapping字段的相关配置

查询实例

查询高亮显示:

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_completionfalse来异步执行,ES以task来描述此类执行任务,
es提供了api来查看任务进度和相关数据。

三、_update_by_query

_update_by_query是指在现有索引上重建,和上面两种都不一样。
Elasticsearch数据建模之——mapping字段的相关配置

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"]
"strict_date_optional_time"
是ISO datetime的格式,完整格式类似为: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,如下所示:
Elasticsearch数据建模之——mapping字段的相关配置

相关标签: Elasticsearch