Elasticsearch Mapping详解(持续更新)
映射
-
动态映射。无须显示指定文档字段数据类型,直接添加数据即可,es会自动推断数据类型,一般在测试时使用,生产过程避免使用字段推断。
-
显式映射。创建索引时显式指定字段和数据类型。
索引创建后不能修改字段名(可以添加alias),不能修改数据类型,可以添加新的字段
-
运行时字段。在查询时确定数据类型,由于不会提前索引数据,可以节省存储空间以及提供更灵活的映射操作,但是在部分搜索API中的表现不同,并且由于运行时确定字段,性能上存在一定损失。
元数据字段
可以理解为es为每个文档添加的实际存在的字段。
-
_doc_count,桶聚合会返回文档个数doc_count字段,通常情况下这个文档数是正确的,但是对于提前聚合的数据类型,如histogram,aggregate_metric_double等,一个字段可能代表了多个文档,此时聚合时不是加1,而是加文档的_doc_count值,默认情况下,_doc_count值为1。
-
_filed_names,曾用于记录一个文档中包含非null值的字段,exists查询的时候直接查这个字段即可。
现在仅用于记录一个文档中doc_values和norms都设置为false的字段,当二者中有任意一个字段可用时,exists查询不会使用_field_names。
doc_values被禁用这个字段很可能不用于排序聚合。
norms被禁用这个字段很可能不用于检索评分。
-
_ignored,记录超出ignore_above或者字段格式不对但ignore_malformed开启的字段。可用于term,terms,exists检索。
-
_id,索引时赋值或者es自动生成,该字段不能进行配置,可用于
term
,terms
,match
,query_string
。最大不能超过512字节。 -
_index,存储索引名,当进行多索引查询时有用。_index是个虚拟字段,不会真正存到Lucene中,所以不支持
regexp
和fuzzy
查询。 -
_meta,自定义的,不被es使用的额外数据信息。比如存储索引的版本、作者、描述信息等。
-
_routing,影响分片的计算,默认的_routing值是文档id,可以为每个文档指定routing值,也可以在检索时指定routing值。默认的分片路由计算是以下公式
routing_factor = index.num_routing_shards / index.num_primary_shards shard_num = (hash(_routing) % index.num_routing_shards) / routing_factor
-
_source,存储原始文档,可以禁用,可以筛选字段。如果磁盘空间比较不够用,可以考虑synthetic _source,节省空间但影响性能,或者禁用_source不存储源文档,但并不建议这样做,会带来以下问题。
- 不支持update、update_by_query、reindex API。
- 不支持高亮。
- 不支持重建索引、映射、文本分析器、升级索引。
- 不支持调试搜索聚合的详细过程。
- 影响自动修复索引。
总之,非特殊情况,空间不够用考虑synthetic _source、compression level而不是禁用_source。
-
_tier,检索时首选想要的数据等级,例如data_hot,data_warm,data_cold。
mapping配置
-
analyzer,分词器,仅用于text字段,如果不显式指定search_analyzer,将同时应用于索引阶段和检索阶段。
-
search_quote_analyzer,phrase查询,不删除停止词。
-
coere,数据类型自动转换,比如字符串转整数,浮点数截断为字符串,设置为false,数据类型与字段值类型必须强制符合。可以对字段单独设置,也可以对索引设置。
-
copy_to,当需要对多个字段进行检索时(组合条件为或),可以使用bool查询,但更建议使用copy_to组合字段,既简单又高效。需要注意以下几点
- copy过去的是字段值,而不是分词后的term。
- copy_to的字段不会包含在_source中,可以理解为仅用于搜索的组合字段。
- 同一字段可以copy到多个组合字段中,数组表示即可。
- 在目标组合字段未指定类型的情况下,如果dynamic为true,使用自动类型推断;如果dynamic为false,copy_to无效;如果dynamic为strict,则会抛出错误。
- copy_to不支持值为对象的数据类型,比如date_range。
- copy到组合数据类型的多个字段最好是同一数据类型,保证搜索行为的一致性,copy_to会忽略原字段的数据类型,对组合的所有字段进行同一种数据类型的检索。
-
doc_values,倒排索引通过term找doc,完成全文检索功能,而对于文档的排序、聚合等操作需要通过doc找terms。Doc values是一种在索引时建立的磁盘数据结构,列式存储,用于提高排序、聚合的效率,几乎支持所有的数据类型,除text和annotated_text,这两种类型也不支持聚合操作。
数值类型、日期类型、布尔类型、ip类型和geo_point类型、keyword类型即使不被索引,通过doc_values也可以被查询,这里的不被索引是指字段的index设置为false。doc_values的查询会显著慢于索引,但节省空间。doc_values适用于查询较少,聚合统计较多的字段,如果一个字段完全不需要排序统计,可以将doc_values设置为false。
-
dynamic,控制es对新增字段的处理,有以下四个选项
- true,默认,添加到mapping。
- runtime,运行时字段,不被索引,查询时从_source计算加载。
- false,新字段不被索引不可查询,但会在_source中出现,不会出现在mapping中。
- strict,拒绝新字段,直接抛出异常。
-
eager_global_ordinals
,基于term的数据类型,比如keyword,整个字段就相当于一个term,通过字典序为term分配一个id,顺序存储的是这个id而不是实际的term,并可以通过id查找到term。顺序存储可以提高聚合的效率。每个索引段有自己的顺序映射结构,但是聚合操作是在整个分片进行的,所以需要es创建了一个分片级的全局顺序映射(global ordinals)维护分片和索引段之间的关系。
全局序和filed data cache共用堆内存,大量数据的聚合操作会占用大量内存。默认情况下,global ordinals在需要用到后才第一次加载,但是如果对于查询聚合速度有要求,可以将设置为eager,即非懒加载模式。当eager_global_ordinals设置为true,global ordinals永远会在索引可用之前被加载,增大建立索引、分片复制等操作的代价。
gloabal ordinals为分片上所有的段提供了一个统一的映射,当有新的段到来时,global ordinals必须重建,因为需要保证有序。所以通常情况下global ordinals不会占用太大的内存,但如果一个分片太大或者字段中有大量唯一的term,加载global ordinals是一个昂贵的操作。
以下操作会用到全局序。
- Certain bucket aggregations on
keyword
,ip
, andflattened
fields. This includesterms
aggregations as mentioned above, as well ascomposite
,diversified_sampler
, andsignificant_terms
. - Bucket aggregations on
text
fields that requirefielddata
to be enabled. - Operations on parent and child documents from a
join
field, includinghas_child
queries andparent
aggregations.
一些情况下可以避免加载global ordinals
- terms、sampler、significant_terms聚合操作支持
execution_hint
参数来控制对桶的聚集。默认是global_ordinals,可以设置为map,直接使用term而不考虑字典序。 - 如果一个分片设置了force-merged合并为一个段,那么段级的ordinals就是global ordinals,不需要额外维护。当一个索引不会再被修改后,你可以强制合并为一个段,否则,修改会抛弃原来整个索引段并重建,代价很高。
- Certain bucket aggregations on
-
enabled,是否会被es解析,索引,只能用于object类型,这能配置在第一层。对于不需要被解析索引的数据,只需要es做存储的,可以配置该选项。
-
format,时间格式化,es提供的时间格式化模板很多,也支持自定义。
-
ignore_above,超过ignore_above长度的term或者term数组的每个term将不会被index或者store。
在Lucene中一个term的最大字节数是32766,ignore_above对字符计数,如果使用UTF-8编码存储非ascii字符,假定每个字符占用四个字节,一个term最长是32766/4=8192个字符。
-
ignore_malformed,忽略数据类型错误的字段,不抛异常,不对该字段index,正常处理其他字段,支持部分字段。可以通过exists、term等查询结果中的_ignore查看有多少个malformed文档。
-
index,是否被索引,数值类型、日期类型、布尔类型、ip类型和geo_point类型、keyword类型即使不被索引,通过doc_values也可以被查询,其他类型index设置为false无法被查询,index和doc_values都设置为false所有数据类型都无法被查询。
-
index_options,控制添加哪些额外信息到倒排索引,只有term-based字段可以设置该值,如text、keyword。有以下四个选项。
- docs,文档id被索引,可以解决文档是否存在term的查询。
- freqs,索引文档id和词频。
- positions,默认选项,文档id、词频和term的位置顺序,在match_phrase查询中很有用。
- offsets,除以上三个外,还有term的字符起始位置,可以提高unified highlighter的速度。
这个选项如果设置为docs应该会影响文档评分?
-
index_phrases,两个term的组合会被index称一个额外字段以提高phrase(不带slop)检索的效率。去除停用词会影响检索效果。默认值为false。
es对中文分词需要插件支持,可能该选项不适合?
两个term比较符合检索的习惯?
-
index_prefixes,对term的前几个字符也进行index以加快prefix检索的效率。
- min_chars,被index的prefix的最小长度,必须大于0,默认为2。
- max_chars,被index的prefix的最大长度,必须小于20,默认为5。
-
meta,可以添加数据的单位、计算方式等额外共享信息。
-
fields,可以理解为字段的子字段,比如一个字段既可以作为text检索,也可以作为keyword排序聚合;一个text字段既使用ik_smart分词,也使用ik_max_word分词。子字段作为一个完全独立的字段存在,不会继承父字段的属性。
可以为mapping字段添加子字段,但对历史数据无效,需要手动调用update_by_query API。
-
normalizer,和analyzer类似,但是保证不会分词,即tokenize阶段只返回一个term的analyzer,对于一些keyword查询,转化小写,去除停用词但是不分词可以配置这个选项,相比于analyzer,不需要配置tokenizer。
-
norms,存储用于计算评分的相关信息,但是占用的磁盘空间较多,如果在某个字段上不需要计算评分,比如仅用于过滤或者聚合的字段,应该禁用此属性。
-
null_value,默认情况下空值是不可index和search的,可以将空值替换为null_value的值,例如NULL字符串或者-1以支持检索。
-
position_increment_gap,当phrase检索多个值时,前一个值的最后一个term和后一个值的第一个term中间的slop数,默认值是100。具体可查看官网例子,比较明了。
-
properties,文档字段描述,可以写在mapping第一层,也可以写在字段中。
-
search_analyzer,检索时的分词器。
-
similarity,相似度评分算法,默认是BM25。
-
store,_source中会把原文档存储一次,某些时候如果想要单独某个字段,需要从_source中去除整个文档再对进行字段过滤,当存在很大的字段时,取出来是比较耗时的。如果不需要这个很大的字段,只需要部分其他字段时,可以考虑将其他字段设置store,es会将这些字段另外存储一份,查询时设置_source为false,从store_fields中取出这些字段,避免大字段的读取。
由于不知道原始字段是单个值还是数组,store后的字段统一返回为数组。
-
subobjects,默认情况下x.y.z会被解析为x对象下的y对象的z属性,如果不想要嵌套下去,在字段的第一层设置该属性为false,x.y.z会被解析为x对象的y.z属性。该设置可以对字段和整个mapping设置,配置后无法修改。
-
term_vector,保存了analysis分词处理的信息。
- 分词的结果。
- 分词结果位置顺序信息。
- 每个词在原始字符串中的起止字符位置。
- 分词器额外输出的一些其他信息,二进制格式。
有以下几个选项配置
- no,不保存这些信息。
- yes,仅保存分词结果。
- with_positions,
- with_offsets,
- with_positions_offsets,
- with_positions_payloads,
- with_positions_offsets_payloads,
with_positions_offsets可以提高vector hignlighter的速度,但是字段的索引大小会直接变为原来的2倍。term vectors API可以查询这些存储的信息。
数据类型
-
Aggregate metric,聚合统计,提前统计聚合的一些值,包含最大值、最小值、数量、和等。
-
Alias,别名,为其它字段添加别名。
-
Arrays,数组,es中没有专门的数组,任何字段都可以包含0个或者多个值,mapping中指定字段的类型为数组的元素类型,不存在array类型,添加文档时可以写成数组的形式,es可以自动识别。
数组中所有值必须是同一数据类型,第一个元素的数据类型决定整个数组的数据类型,数组中的null值可以配置为忽略或者null_value类型。
-
Binary,二进制,接受base64字符串,默认是不store并且不会被index。
-
Boolean,布尔值。
-
Completion,自动补全,输入term的一部分,返回一系列可选的term用于补全,可配置参数比较多,如补全来自哪些字段,前置和后置过滤等。
-
Date,日期,毫秒存储。
-
Date nanoseconds,纳秒存储,支持范围1970到2262。
-
Dense vector,密集向量,其含义及详细配置可查看这篇文章。
-
Flattened,默认情况下,对象的子字段会被单独映射和索引,如果子字段的名称和类型没有提前确定,会使用自动映射。使用Flattened会将对象的所有叶子节点的值作为keyword索引到一个字段中,这个对象里的所有属性可以被查询和聚合。当不指定子字段时,会查询所有叶子节点,也可以使用属性.属性的方式查询某个特定属性。
-
Geopoint,地理坐标。
-
Geophape,地理范围。
-
Histogram,数据预先聚合过的直方图。
-
IP,IPV4或者IPV6。
-
Join,可以维护同一索引中不同文档之间的父子关系,支持连接查询。
-
Keyword,适用于term级的查询,有三种子类型
- keyword,结构化的数据,比如ID、邮件地址、标签、域名等。
- constant_keyword,固定不变的。
- wildcard,适合非结构化的,机器生成的数据,比如日志,对于大字段做了优化。
keyword类型配置normalizer,可以在被所引之前进行前置处理,默认不进行任何处理。
-
Nested,object的特殊版本,允许对象数组。
-
Numeric,数值类型。
-
Object,json对象类型。
-
Percolator, 和percolate query一起工作。
-
Point,二维数据点。
-
Range,范围。
-
Rank feature,和rank_feature一起工作,可以影响文档评分。
-
Rank features,可以指定多个影响评分的字段。
-
Search-as-your-type,类似 text,创建一系列子字段,支持前缀完成(即,匹配项从输入的开头开始)和中缀完成(即,匹配项在输入中的任意位置)。
-
Shape,图形形状。
-
Sparser vector,稀疏向量。
-
Text,包括text和match_only_text,后者相当于index_options=docs和norms=false的text类型,节省了存储空间,加快了term级查询,降低了phrase级查询和评分的效率。
-
Token count,接收一个字符串,存储的是分词后的数量,可以用在text字段的子字段中。
-
Unsigned long,无符号长整型。
-
Version,版本号。
keyword和text如何选择
二者都可以存储文本字段,如何选择取决于数据格式和搜索需求。
使用text的情况
- 文本是可读可理解的非结构化数据,比如小说内容、产品描述等。
- 需要全文检索相关文档。
使用keyword情况
- 邮件地址、IP或者程序日志,有一定结构的非结构化数据。
- 检索整个值或者模糊检索部分,例如org.foo.*,正则表达式匹配等。
如何创建mapping以优化存储
可以按以下几个步骤进行
- 根据需求确定数据类型
- 对每个字段考虑
- 是否需要索引,确定index。
- 是否需要评分,确定norms。
- 是否会用于排序聚合,确定doc_values。
- 是否需要分词以及如何分词,确定analyzer,search_analyzer,quote_search_analyzer。
- 是否需要高亮以及何种方式高亮,确定index_options和term_vector,本人使用的是默认值。
- 是否需要在查询时stored_fileds,确定store,绝大多数情况下使用_source filter即可。
- 是否有组合字段,确定copy_to,以及组合字段如何配置。
- 根据需求确定setting中的analysis配置,包括filter,char_filter,tokenizer,normalizer等配置。
本人考虑以上优化后,在测试文档上查看索引占用从1.5G下降到了527MB。
以上是个人比较常用的一些配置,其余可按需要修改。