概述

当遇到,类似如下报错:

Can't load fielddata on [product_id] because fielddata is unsupported on fields of type [long]. Use doc values instead

读完这篇文章,你将知道如何解决这个问题。

搜索vs分析

大家知道,搜索引擎的基本数据结构是反向索引,也就是为每个关键词建立了到文档的映射,然后所有的关键词是一个有序列表。搜索的时候,只要先从有序列表中匹配到关键词,就能搜索到包含该关键词的所有文档,反向索引的数据结构对于关键词搜索的场景是非常高效的。

但聚合分析和搜索有很大的不同。典型的场景,比如计算某个文档中每个关键词的出现次数,反向索引就无能为力了,需要先扫描整个关键词映射表,才能找到该文档包含的所有关键词,然后再进行聚合统计(这个例子其实不太准确,因为Lucene在反向索引中冗余了词频的信息,用于计算搜索相关度),也就是要对整个反向索引做全扫描,在数据量大的时候,性能当然好不到哪里去。

所以,Elasticsearch为聚合计算引入了名为fielddata的数据结构,其实就是根据反向索引再次反向出来的一个正向索引,也就是文档到关键词的映射。因为聚合计算也好,排序也好,通常是针对某些列的,实际上生成的是文档到field的多个列式索引,所以叫做fielddata。这样对文档内的关键词做聚合计算的时候,就只要从fielddata中根据文档ID查找就好。而且,fielddata是保存在内存中的,好处是不占用存储,坏处么,当然上内存不够用啦。而且这个内存是从JVM的Heap上分配的,因为JVM对于大内存的垃圾收集的影响,不能不说对稳定性有很大的挑战,数据量大的时候,时不时的OutOfMemory也不是闹着玩的。因为内存是有限的,所以不可能预先为所有的字段都建立fielddata,只能是由具体的搜索需求来触发。如果是未命中的搜索,还需要先在内存中建立fielddata,这会影响到响应时间。

fielddata的问题在于内存的有限性和JVM对于大内存的垃圾收集对系统带来的稳定性挑战。所以后来又引入了一个新的机制,就是DocValues,从数据结构上来说,它和fielddata是一样的按列的正向索引,但是实现方式不同,DocValues是持久化存储在文件中,并且是预先构建的,也就是数据进入到Elasticsearch时,就会同时生成反向索引和DocValues,这会消耗额外的存储空间,但对于JVM的内存需求会大幅度减少,剩余的内存可以留给操作系统的文件缓存使用。加上DocValues是预先构建的,查询时也免去了不命中时构建fielddata的时间,所以总体来看,DocValues只比内存fielddata慢大概10~25%,稳定性则有了大幅度提升。

从Elasticsearch2.0开始,除了text 类型分词过的字符串字段,其他字段已经默认生成DocValues了(可以在索引的Mapping中通过doc_values布尔值来设置)。

Doc-value-only fields

Numeric types, date types, the boolean type, ip type, geo_point type and the keyword type can also be queried when they are not indexed but only have doc values enabled. Query performance on doc values is much slower than on index structures, but offers an interesting tradeoff between disk usage and query performance for fields that are only rarely queried and where query performance is not as important. This makes doc-value-only fields a good fit for fields that are not expected to be normally used for filtering, for example gauges or counters on metric data.

简单讲,Elasticsearch通过反向索引做搜索,通过DocValues列式存储做分析,将搜索和分析的场景统一到了通一个分布式系统中,前景还是很不错的。

不过,分析不仅仅是聚合,这也是Elasticsearch还需要继续努力的方向。目前通过Elasticsearch-Hadoop项目,可以将Elasticsearch的搜索结果做为Spark的RDD,利用Spark做更深度的分析。未来如果分布式计算这一层能够和Spark这样的计算框架再进一步做深度的融合,恐怕有可能成为大数据领域内的另外一个大杀器。

ES 做分析的原理:Doc Values

Doc Values 是在索引时与 倒排索引 同时生成。也就是说 Doc Values 和 倒排索引 一样,基于 Segement 生成并且是不可变的。同时 Doc Values 和 倒排索引 一样序列化到磁盘,这样对性能和扩展性有很大帮助。

Doc Values 通过序列化把数据结构持久化到磁盘,我们可以充分利用操作系统的内存,而不是 JVM 的 Heap 。当 working set 远小于系统的可用内存,系统会自动将 Doc Values 驻留在内存中,使得其读写十分快速;不过,当其远大于可用内存时,系统会根据需要从磁盘读取 Doc Values,然后选择性放到分页缓存中。很显然,这样性能会比在内存中差很多,但是它的大小就不再局限于服务器的内存了。如果是使用 JVM 的 Heap 来实现那么只能是因为 OutOfMemory 导致程序崩溃了。

因为 Doc Values 不是由 JVM 来管理,所以 Elasticsearch 实例可以配置一个很小的 JVM Heap,这样给系统留出来更多的内存。同时更小的 Heap 可以让 JVM 更加快速和高效的回收。

之前,我们会建议分配机器内存的 50% 来给 JVM Heap。但是对于 Doc Values,这样可能不是最合适的方案了。以 64gb 内存的机器为例,可能给 Heap 分配 4-16gb 的内存更合适,而不是 32gb

目前,除了字符串,ES 有两种family类型:keywordtext

其他类型系列只有一个字段类型, 例如,boolean类型族由一种字段类型组成:boolean

Doc Values 列式存储的压缩

从广义来说,Doc Values 本质上是一个序列化的 列式存储 。正如我们上一节所讨论的,列式存储 适用于聚合、排序、脚本等操作。

而且,这种存储方式也非常便于压缩,特别是数字类型。这样可以减少磁盘空间并且提高访问速度。现代 CPU 的处理速度要比磁盘快几个数量级(尽管即将到来的 NVMe 驱动器正在迅速缩小差距)。所以我们必须减少直接存磁盘读取数据的大小,尽管需要额外消耗 CPU 运算用来进行解压。

要了解它如何压缩数据的,来看一组数字类型的 Doc Values

Doc      Terms
-----------------------------------------------------------------
Doc_1 | 100
Doc_2 | 1000
Doc_3 | 1500
Doc_4 | 1200
Doc_5 | 300
Doc_6 | 1900
Doc_7 | 4200
-----------------------------------------------------------------

按列布局意味着我们有一个连续的数据块: [100,1000,1500,1200,300,1900,4200] 。因为我们已经知道他们都是数字(而不是像文档或行中看到的异构集合),所以我们可以使用统一的偏移来将他们紧紧排列。

而且,针对这样的数字有很多种压缩技巧。你会注意到这里每个数字都是 100 的倍数,Doc Values 会检测一个段里面的所有数值,并使用一个 最大公约数 ,方便做进一步的数据压缩。

如果我们保存 100 作为此段的除数,我们可以对每个数字都除以 100,然后得到: [1,10,15,12,3,19,42] 。现在这些数字变小了,只需要很少的位就可以存储下,也减少了磁盘存放的大小。

Doc Values 在压缩过程中使用如下技巧。它会按依次检测以下压缩模式:

  1. 如果所有的数值各不相同(或缺失),设置一个标记并记录这些值

  2. 如果这些值小于 256,将使用一个简单的编码表

  3. 如果这些值大于 256,检测是否存在一个最大公约数

  4. 如果没有存在最大公约数,从最小的数值开始,统一计算偏移量进行编码

你会发现这些压缩模式不是传统的通用的压缩方式,比如 DEFLATE 或是 LZ4。因为列式存储的结构是严格且良好定义的,我们可以通过使用专门的模式来达到比通用压缩算法(如 LZ4 )更高的压缩效果。

你也许会想 "好吧,貌似对数字很好,不知道字符串怎么样?" 通过借助顺序表(ordinal table),String 类型也是类似进行编码的。String 类型是去重之后存放到顺序表的,通过分配一个 ID,然后通过数字类型的 ID 构建 Doc Values。这样 String 类型和数值类型可以达到同样的压缩效果。

顺序表本身也有很多压缩技巧,比如固定长度、变长或是前缀字符编码等等。

禁用 Doc Values

Doc Values 默认对所有字段启用,除了 analyzed strings。也就是说所有的数字、地理坐标、日期、IP 和不分析( not_analyzed )字符类型都会默认开启。

analyzed strings 暂时还不能使用 Doc Values。文本经过分析流程生成很多 Token,使得 Doc Values 不能高效运行。我们将在 聚合与分析 讨论如何使用分析字符类型来做聚合。

因为 Doc Values 默认启用,你可以选择对你数据集里面的大多数字段进行聚合和排序操作。但是如果你知道你永远也不会对某些字段进行聚合、排序或是使用脚本操作?尽管这并不常见,但是你可以通过禁用特定字段的 Doc Values 。这样不仅节省磁盘空间,也许会提升索引的速度。

要禁用 Doc Values ,在字段的映射(mapping)设置 doc_values: false 即可。例如,这里我们创建了一个新的索引,字段 "session_id" 禁用了 Doc Values

PUT my_index
{"mappings": {"my_type": {"properties": {"session_id": {"type":       "string","index":      "not_analyzed","doc_values": false }}}}
}

通过设置 doc_values: false ,这个字段将不能被用于聚合、排序以及脚本操作

反过来也是可以进行配置的:让一个字段可以被聚合,通过禁用倒排索引,使它不能被正常搜索,例如:

PUT my_index
{"mappings": {"my_type": {"properties": {"customer_token": {"type":       "string","index":      "not_analyzed","doc_values": true, "index": "no" }}}}
}

Doc Values 被启用来允许聚合

索引被禁用了,这让该字段不能被查询/搜索

通过设置 doc_values: true 和 index: no ,我们得到一个只能被用于聚合/排序/脚本的字段。无可否认,这是一个非常少见的情况,但有时很有用。

ES 常见类型

ElasticSearch (ES)字段类型,按family分组。同一族中的类型具有完全相同的搜索行为,但可能具有不同的空间使用或性能特征。


binary
编码为 Base64 字符串的二进制值。该字段默认 不存储到索引 且 不可搜索。

参数 意义
doc_values 该字段是否应该以列的方式存储在磁盘上,以便以后可以用于排序、聚合或脚本?默认为false
store 字段值是否应与字段_source分开存储和检索。接受truefalse (默认)。

参数讲解:

binary数据类型使用:

  • PUT my-index-000001
    {"mappings": {"properties": {"blob": {"type": "binary","doc_values": false,"store": false}}}
    }
  1. doc_values:倒排索引这种数据结构并不适合用作聚合,排序,在脚本里访问字段等等这些场景,于是 doc_values 数据结构应运而生。doc_values 为 true 时,es同时会以列的方式保存一份数据用作聚合,排序。


boolean
true 或者 false

参数 意义
doc_values 该字段是否应该以列的方式存储在磁盘上,以便以后可以用于排序、聚合或脚本
index 该字段是否应该可以快速搜索。接受true(默认)和 false. 仅doc_values 启用的字段仍然可以使用基于术语或范围的查询进行查询,尽管速度较慢。
null_value 当文档中该字段为空时,这个值会当作默认值填充到字段中。默认为null,这意味着该字段被视为缺失。请注意,如果使用参数,则 script 无法设置。
script 字段值将基于脚本转换所得, 不再依赖doc文档中的输入。
on_script_error script中的脚本运行失败时的策略。默认为 fail, 返回doc文档插入失败。
store 字段值是否应与字段_source分开存储和检索。接受truefalse (默认)。
meta 关于字段的元数据。

Keywords
Keywords关键字族,包括 keywordconstant_keywordwildcard

关键字字段,通常用于排序, 聚合, 术语级别的查询 ,例如,term



  • wildcard
    用于非结构化机器生成的内容, 例如日志等一些需要经常正则匹配查询的内容。

  • constant_keyword
    适用于始终包含相同值的关键字字段, 例如 枚举值。

  • keyword
    适用于 结构化内容 ,例如 ID、电子邮件地址、主机名、状态代码、邮政编码或标签。

    参数 意义
    doc_values 该字段是否应该以列的方式存储在磁盘上,以便以后可以用于排序、聚合或脚本。默认为true
    eager_global_ordinals 是否应该在刷新时急切地加载全局序数?接受true或false (默认)。对于常用于术语聚合的字段建议开启。
    fields 多字段允许为不同目的以多种方式索引相同的字符串值,例如一个用于搜索的字段和一个用于排序和聚合的多字段。
    ignore_above 不要索引任何长于该值的字符串。默认为,2147483647 以便接受所有值。但是请注意,默认动态映射规则会创建一个子keyword字段,通过设置覆盖此默认值ignore_above: 256
    index 该字段是否应该可以快速搜索?接受true(默认)和 false. 仅doc_values 启用的字段仍然可以使用基于术语或范围的查询进行查询,尽管速度较慢。
    index_options 出于评分目的,应将哪些信息存储在索引中。默认为docs但也可以设置为freqs在计算分数时考虑词频。
    meta 关于字段的元数据。
    norms 字段长度是否对查询评分有影响。接受true或false(默认)。
    null_value 当文档中该字段为空时,这个值会当作默认值填充到字段中。默认为null,这意味着该字段被视为缺失。请注意,如果使用参数,则 script 无法设置。
    script 字段值将基于脚本转换所得, 不再依赖doc文档中的输入。
    on_script_error script中的脚本运行失败时的策略。默认为 fail, 返回doc文档插入失败。
    store 字段值是否应与字段_source分开存储和检索。接受truefalse (默认)。
    similarity 应该使用 哪种评分算法或相似度。默认为BM25.
    normalizer 如何在索引之前对关键字进行预处理。默认为null,表示关键字保持原样。
    split_queries_on_whitespace 为该字段构建查询时,全文查询是否应在空格上拆分输入。接受true或false(默认)。

    参数讲解:

  1. eager_global_ordinals:为了支持字段值的聚合和其他操作,Elasticsearch 使用了一种称为 doc values 的数据结构。在基于字段类型为术语的场景下, 使用 增量整数或序数 替代 相同的术语 可以使 doc values 存储更紧凑。

  2. fields:给字段设置额外的类型和索引方式。例如主类型为text用于全文搜索,同时设置一个keyword用于排序或聚合。

  3. ignore_above :超过ignore_above值所标记的长度,则不会被存储和索引(不影响_source的原始数据)。默认为 2147483647, 基本可接受全部长度。

  4. index:该字段是否应该被快速搜索(被倒排索引存储),接受true(默认)和 false。如果字段 doc_values 存在仍然可以被查询, 只是相对比慢一些(doc_values对应的是正排索引)。


Numbers
数字类型,例如 longdouble ,用于表示金额。

PUT my-index-000001
{"mappings": {"properties": {"number_of_bytes": {"type": "integer"},"time_in_seconds": {"type": "float"},"price": {"type": "scaled_float","scaling_factor": 100}}}
}
参数 意义
coerce 尝试将字符串转换为数字并截断整数的分数。接受true(默认)和false.
doc_values 该字段是否应该以列的方式存储在磁盘上,以便以后可以用于排序、聚合或脚本。
ignore_malformed 是否忽略错误格式的数字。如果false(默认),格式错误的数字会引发异常并拒绝整个文档。
index 该字段是否应该可以快速搜索?接受true(默认)和 false. 仅doc_values 启用的字段仍然可以使用基于术语或范围的查询进行查询,尽管速度较慢。
null_value 当文档中该字段为空时,这个值会当作默认值填充到字段中。默认为null,这意味着该字段被视为缺失。请注意,如果使用参数,则 script 无法设置。
script 字段值将基于脚本转换所得, 不再依赖doc文档中的输入。
on_script_error script中的脚本运行失败时的策略。默认为 fail, 返回doc文档插入失败。
store 字段值是否应与字段_source分开存储和检索。接受truefalse (默认)。
meta 关于字段的元数据。
  • Dates
    日期类型,包括datedate_nanos

  • alias
    映射定义索引中字段的alias备用名称。别名可用于代替搜索请求中的目标字段。别名的使用限制:

    • 字段别名只能有一个目标

    • 如果定义了嵌套对象,则字段别名必须具有与其目标相同的嵌套范围。

    • 目标字段在创建别名时必须存在。

    • 目标必须是具体字段,而不是对象或其他字段别名。


对象和关系类型

object
一个 JSON 对象。

参数 意义
dynamic 是否允许在插入文档doc时由动态映射为object的properties新增属性。接受true(默认)runtime、false 和strict。
enabled 为对象字段提供的 JSON 值是否应该被解析和索引(true默认)或完全忽略(false)。
properties 对象内的字段,可以是任何数据类型,包括object。可以将新属性添加到现有对象。
  • PUT my-index-000001
    {"mappings": {"properties": {"region": {"type": "keyword"},"manager": {"properties": {"age":  { "type": "integer" },"name": {"properties": {"first": { "type": "text" },"last":  { "type": "text" }}}}}}}
    }
  • flattened
    默认情况下, ES会为object中每个子字段进行映射和索引(如果事先不知道子字段的名称和类型, 则动态映射它们)。这种情况下, 如果这个object类型的对象存在大量的子字段, 会导致子字段映射爆炸。而flattened类型就是为这种情况应运而生的。

    ES会把flattened类型的JSON对象作为单个字段,只把它的 叶子值 作为关键字索引一个字段中。然后可以通过简单的查询和聚合来搜索对象的内容。

    参数 意义
    depth_limit 就嵌套内部对象而言,展平对象字段的最大允许深度。如果展平的对象字段超过此限制,则会引发错误。默认为20
    doc_values 该字段是否应该以列的方式存储在磁盘上,以便以后可以用于排序、聚合或脚本。
    eager_global_ordinals 是否应该在刷新时急切地加载全局序数?接受true或false (默认)。对于常用于术语聚合的字段建议开启。
    ignore_above 超过此限制的叶值将不会被索引。默认情况下,没有限制,所有值都将被索引。请注意,此限制适用于展平对象字段中的叶值,而不是整个字段的长度。
    index 确定该字段是否应该是可搜索的。接受true(默认)或 false.
    index_options 出于评分目的,应将哪些信息存储在索引中。默认为docs但也可以设置为freqs在计算分数时考虑词频。
    null_value 当文档中该字段为空时,这个值会当作默认值填充到字段中。默认为null,这意味着该字段被视为缺失。请注意,如果使用参数,则 script 无法设置。
    similarity 应该使用 哪种评分算法或相似度。默认为BM25.
    split_queries_on_whitespace 为该字段构建查询时,全文查询是否应在空格上拆分输入。接受true或false(默认)。
  • PUT bug_reports
    {"mappings": {"properties": {"title": {"type": "text"},"labels": {"type": "flattened"}}}
    }POST bug_reports/_doc/1
    {"title": "Results are not sorted correctly.","labels": {"priority": "urgent","release": ["v1.2.5", "v1.3.0"],"timestamp": {"created": 1541458026,"closed": 1541457010}}
    }# 这里的查询直接以 ‘labels’ 作为关键字!!!
    POST bug_reports/_search
    {"query": {"term": {"labels": "urgent"}}
    }
  • nested
    nested类型是object数据类型的特殊版本,它允许对对象数组进行索引,从而可以相互独立地查询它们。

    默认情况下, object数据类型对于数组的映射和索引使用的是扁平化展开的方式。

PUT my-index-000001/_doc/1{"group" : "fans","user" : [{"first" : "John","last" :  "Smith"},{"first" : "Alice","last" :  "White"}]
}

实际上,上面文档将在内部转换为看起来更像这样的文档:

{"group":        "fans","user.first": [ "alice", "john" ],"user.last": [ "smith", "white" ]
}

为了让object类型字段内的数组对象之间保持独立性,nested便应运而生了!!!

  • 参数 意义
    dynamic 是否允许在插入文档doc时由动态映射为object的properties新增属性。接受true(默认)runtime、false 和strict。
    properties 对象内的字段,可以是任何数据类型,包括object。可以将新属性添加到现有对象。
  • join
    为同一索引中的文档定义父/子关系。

    PUT my-index-000001
    {"mappings": {"properties": {"my_id": {"type": "keyword"},"my_join_field": {"type": "join","relations": {"question": "answer"}}}}
    }
    PUT my-index-000001/_doc/1?refresh
    {"my_id": "1","text": "This is a question","my_join_field": {"name": "question"}
    }PUT my-index-000001/_doc/1?refresh
    {"my_id": "1","text": "This is a question","my_join_field": "question"
    }
    PUT my-index-000001/_doc/3?routing=1&refresh
    {"my_id": "3","text": "This is an answer","my_join_field": {"name": "answer","parent": "1" }
    }
    1.  `?routing=parent-doc-id`路由值是强制性的,因为父文档和子文档必须在同一个分片上建立索引。
    2.  `"answer"`为 `parent/child` 关系中的`child`, 因此必须添加`"parent"`字段, 且填充`parent/child` 关系中的`parent-id`。
    3.  在`插入parent-doc`的例子里面, 没有添加`"parent"`字段。因为`"question"`是`parent/child` 关系中的`parent`,插入`parent-doc`时, ES会默认填充`_id`到`"parent"`字段中。
    • join 使用限制

    • 使用 join 搜索

      GET my-index-000001/_search
      {"query": {"parent_id": { "type": "answer","id": "1"}},"aggs": {"parents": {"terms": {"field": "my_join_field#question", "size": 10}}},"runtime_mappings": {"parent": {"type": "long","script": """emit(Integer.parseInt(doc['my_join_field#question'].value)) """}},"fields": [{ "field": "parent" }]
      }
    • 插入child-doc

    • 插入parent-doc的两种方式

    • mapping定义,relations:{ "parent-field" : "child-field"}

  1. join 会创建一个字段来索引文档中的关系名称, 例如question, answer

  2. 它还为每个parent/child关系创建一个字段, 例如"my_join_field#question" , 如果该文档为parent-doc:question则为自身_id, 如果该文档为child-doc:answer_id为链接到父文档。

  3. 一个索引最多只能有一个join 类型字段

  4. parent-docchild-doc 必须在同一个shard分片中

  5. 一个元素可以有多个子元素,但只有一个父元素。

  6. 可以向现有join字段添加新关系。

  7. 也可以将子元素添加到现有元素,但前提是该元素已经是父元素。

ES常见配置项说明:

在很多场合下,我们并不需要存储上述全部信息,因此可以通过设置 mappings 里面的属性来控制哪些字段是我们需要存储的、哪些是不需要存储的。而 ES 的 mapping 中有很多设置选项,这些选项如果设置不当,有的可能浪费存储空间,有的可能导致无法使用 Aggregation,有的可能导致不能检索。下面就简单介绍下 ES 中常见的存储与检索的 mapping 配置项:

在ES的 mapping 设置里,all,source 是 mapping 的元数据字段(Meta-Fields),store、doc_values、enabled、index 是 mapping 参数。

1、_all:

all 字段的作用是提供跨字段查询的支持,把 mapping 中的所有字段通过空格拼接起来做索引。ES在查询的过程中,需要指定在哪一个field里面查询。

{“name”: “smith”,“email”: "John@example.com"
}

用户在查询时,想查询叫做 John 的人,但不知道 John 出现在 name 字段中还是在 email 字段中,由于ES是为每一个字段单独建立索引,所以用户需要以 John 为关键词发起两次查询,分别查询name字段和email字段。

如果开启了 all 字段,则ES会在索引过程中创建一个虚拟的字段 all,其值为文档中各个字段拼接起来所组成的一个很长的字符串(例如上面的例子,all 字段的内容为字符串 “smith John@example.com”)。随后,该字段将被分词打散,与其他字段一样被收入倒排索引中。由于 all 字段包含了所有字段的信息,因此可以实现跨字段的查询,用户不用关心要查询的关键词在哪个字段中。

由于该字段的内容都来自 source 字段,因此默认情况下,该字段的内容并不会被保存,可以通过设置 store 属性来强制保存 all 字段。开启 all 字段,会带来额外的CPU开销和存储,如果没有使用到,可以关闭 all 字段。

2、_source:

source 字段用于存储 post 到 ES 的原始 json 文档。为什么要存储原始文档呢?因为 ES 采用倒排索引对文本进行搜索,而倒排索引无法存储原始输入文本。一段文本交给ES后,首先会被分析器(analyzer)打散成单词,为了保证搜索的准确性,在打散的过程中,会去除文本中的标点符号,统一文本的大小写,甚至对于英文等主流语言,会把发生形式变化的单词恢复成原型或词根,然后再根据统一规整之后的单词建立倒排索引,经过如此一番处理,原文已经面目全非。因此需要有一个地方来存储原始的信息,以便在搜到这个文档时能够把原文返回给查询者。

那么一定要存储原始文档吗?不一定!如果没有取出整个原始 json 结构体的需求,可以在 mapping 中关闭 source 字段或者只在 source 中存储部分字段(使用store)。但是这样做有些负面影响:

(1)不能获取到原文

(2)无法reindex:如果存储了 source,当 index 发生损坏,或需要改变 mapping 结构时,由于存在原始数据,ES可以通过原始数据自动重建index,如果不存 source 则无法实现

(3)无法在查询中使用script:因为 script 需要访问 source 中的字段

3、store:

store 决定一个字段是否要被单独存储。大家可能会有疑问,source 里面不是已经存储了原始的文档嘛,为什么还需要一个额外的 store 属性呢?原因如下:

(1)如果禁用了 source 保存,可以通过指定 store 属性来单独保存某个或某几个字段,而不是将整个输入文档保存到 source 中。

(2)如果 source 中有长度很长的文本(如一篇文章)和较短的文本(如文章标题),当只需要取出标题时,如果使用 source 字段,ES需要读取整个 source 字段,然后返回其中的 title,由此会引来额外的IO开销,降低效率。此时可以选择将 title 的 store 设置为true,在 source 字段外单独存储一份。读取时不必在读取整 source 字段了。但是需要注意,应该避免使用 store 查询多个字段,因为 store 的存储在磁盘上不连续,ES在读取不同的 store 字段时,每个字段的读取均需要在磁盘上进行查询操作,而使用 source 字段可以一次性连续读取多个字段。

4、doc_values:

倒排索引可以提供全文检索能力,但是无法提供对排序和数据聚合的支持。doc_values 本质上是一个序列化的列式存储结构,适用于聚合(aggregations)、排序(Sorting)、脚本(scripts access to field)等操作。

默认情况下,ES几乎会为所有类型的字段存储doc_value,但是 text 或 text_annotated 等可分词字段不支持 doc values 。

如果不需要对某个字段进行排序或者聚合,则可以关闭该字段的doc_value存储(doc_values: false)

5、index:

控制倒排索引,用于标识指定字段是否需要被索引。默认情况下是开启的,如果关闭了 index,则该字段的内容不会被 analyze 分词,也不会存入倒排索引,即意味着该字段无法被搜索。

6、enabled:

这是一个 index 和 doc_value 的总开关,如果 enabled 设置为false,则这个字段将会仅存在于 source 中,其对应的 index 和 doc_value 都不会被创建。这意味着,该字段将不可以被搜索、排序或者聚合,但可以通过 source 获取其原始值。

7、term_vector:

在对文本进行 analyze 的过程中,可以保留有关分词结果的相关信息,包括单词列表、单词之间的先后顺序、单词在原文中的位置等信息。查询结果返回的高亮信息就可以利用其中的数据来返回。默认情况下,term_vector是关闭的,如有需要(如加速highlight结果)可以开启该字段的存储。

doc_values 详细说明:

1、doc_values 的作用:

基于 lucene 的 solr 和 es 都是使用倒排索引实现快速检索的,也就是通过建立 "搜索关键词 ==>文档ID列表" 的关系映射实现快速检索,但是倒排索引也是有缺陷的,比如我们需要字段值做一些排序、分组、聚合操作,lucene 内部会遍历提取所有出现在文档集合的排序字段,然后再次构建一个最终的排好序的文档集合list,这个步骤的过程全部维持在内存中操作,而且如果排序数据量巨大的话,非常容易就造成solr内存溢出和性能缓慢。

doc values 就是在构建倒排索引时,会对开启 doc values 的字段额外构建一个有序的 "document文档 ==> field value“ 的列式存储映射,从而实现对指定字段进行排序和聚合时对内存的依赖,提升该过程的性能。

默认情况下每个字段的 doc values 有开启、关闭的默认配置 (根据不同字段类型,),当然 doc values 也会耗费一定的磁盘空间。

另外 doc values 保存在操作系统的磁盘中,当 doc values 大于节点的可用内存,ES 可以从操作系统页缓存中加载或弹出,从而避免发生 JVM 内存溢出的异常,docValues 远小于节点的可用内存,操作系统自然将所有Doc Values存于内存中(堆外内存),有助于快速访问。

2、doc_values 与 source 的区别?使用 docvalue_fields 检索指定的字段?

post 提交到 ES 的原始 Json 文档都存储在 source 字段中,默认情况下,每次搜索的命中结果都包含文档 source,即使仅请求少量字段,也必须加载并解析整个 source 对象,而 source 每次使用时都必须加载和解析,所以使用 source 非常慢。为避免该问题,当我们只需要返回相当少的支持 doc_values 的字段时,可以使用 docvalue_fields 参数获取选定字段的值。

doc values 存储与 _source 相同的值,但在磁盘上基于列的结构中进行了优化,以进行排序和汇总。由于每个字段都是单独存储的,因此 Elasticsearch 仅读取请求的字段值,并且可以避免加载整个文档 _source。通过 docvalue_fields 可以从建好的列式存储结果中直接返回字段值,毕竟 source 是从一大片物理磁盘去,理论上从 doc values 处拿这个字段值会比 source 要快一点,页面抖动少一点。

3、如何在 ES 中使用 doc values?

doc values 通过牺牲一定的磁盘空间带来的好处主要有两个:

1)节省内存; 2)提升排序、分组等聚合操作的性能

那么我们如何使用 doc values 呢?

(1)我们首先关注如何激活 doc values,只要开启 doc values 后,排序,分组,聚合的时候会自动使用 doc values 提速。在 ElasticSearch 中,doc values 默认是开启的,比较简单暴力,我们也可以酌情关闭一些不需要使用 doc values 的字段,以节省磁盘空间,只需要设置 doc_values 为 false 就可以了,如下:

"session_id":{"type":"string","index":"not_analyzed","doc_values":false}

(2)使用 docvalue_fields 的检索指定的字段:

GET my-index-000001/_search
{"query": {"match": {"user.id": "kimchy"}},"docvalue_fields": ["user.id","http.response.*", {"field": "date","format": "epoch_millis" }]
}

参考资料

https://blog.csdn.net/a745233700/article/details/117915118

https://www.jianshu.com/p/42aabf6f99c8

https://www.elastic.co/guide/cn/elasticsearch/guide/current/docvalues-intro.html

https://www.elastic.co/guide/cn/elasticsearch/guide/current/docvalues.html

https://www.elastic.co/guide/cn/elasticsearch/guide/current/_deep_dive_on_doc_values.html

https://www.cnblogs.com/dtstack/p/9760459.html

ElasticSearch - Mapping 配置字段属性:对 long 类型字段聚合需要开启 doc_values:true相关推荐

  1. es 指定排序字段_ES使用text类型字段排序报错

    elasticsearch text字段排序报错解决 使用elasticsearch 进行排序的时候,我们一般都会排序数字.日期.但是在排序text类型的时候就会出现错误. GET xytest/su ...

  2. mysql 字段属性命令_mysql 操作字段 命令

    ALTER TABLE - 更改表属性 添加字段: alter table `user_movement_log` Add column GatewayId int  not null default ...

  3. 数据库常用字段、列属性、表类型与SQLyog工具的使用

    目录 SQLyog工具 数据库的一些基本操作命令 结构化查询语句 数据值和列类型 列类型 数据字段属性 数据表类型 数据表的存储位置 : SQLyog工具 一款可视化操作数据库的工具,简洁,易用,图形 ...

  4. Odoo 模型、py字段属性、view字段属性使用

    数据模型的设计: 区分实体模型.瞬态模型.抽象模型. 实体模型:数据需要持久化存储在数据库中. 瞬态模型:向导,临时数据,不需要持续化在数据库中的数据. 抽象模型:定义的底层模型.供其他实体模型继承的 ...

  5. MySQL数据类型及字段属性

    MySQL 数据类型MySQL中定义数据字段的类型对你数据库的优化是非常重要的.MySQL支持多种类型,大致可以分为三类: 数值 日期/时间 字符串(字符) 数值类型 MySQL支持所有标准SQL数值 ...

  6. oracle,mysql常用基本操作 字段属性

    1.Oracle 1.向数据表添加注释及查询表注释 SELECT * FROM USER_TAB_COMMENTS WHERE TABLE_NAME='表名'; --查询注释 COMMENT ONTA ...

  7. ARCGIS中按照某字段属性批量导出shp ,即将一个shp图层分割成单个的shp

    如何通过某字段属性批量导出各个字段对应的shp文件?比如我现有一副国家的的行政区划图,想要提取各个省份的shp文件. 通过Analysis Tools--Extract--Split工具实现. 步骤如 ...

  8. odoo13中的模型类(模型属性、字段属性、字段类型)

    目录 1.模型属性 2.字段类型 3.字段通用参数属性 1.模型属性 模型属性 解释 _name 模型名称.使用点表示法.规范命名带模块命名空间,格式:模块名.xxx.xxx.xxx: _descri ...

  9. SAP MM PR单据类型的配置里‘Control’和’Doc.Type’字段的作用?

    SAP MM PR单据类型的配置里'Control'和'Doc.Type'字段的作用? Control:如果选成了T,是使用于STO(公司间转储或者公司内转储),需要将Plant当作VENDOR. C ...

最新文章

  1. GT Transceiver的动态重配置端口
  2. VR规格表出来啦!Rift,Vive,PSVR,Acer和HP
  3. java 之持久化和序列化(反序列化)
  4. Zabbix学习之路(一)之Zabbix安装
  5. MQ消息队列产品测试
  6. 使用verilog设计实现QR分解
  7. mysql+rsyslog+loganalyzer的部署方案
  8. 图像处理技术(三)白平衡
  9. 智慧交通day02-车流量检测实现11:yoloV3模型
  10. linux php oracle 乱码,linux安装oracle出现界面乱码
  11. 十五天精通WCF——第十三天 用WCF来玩Rest
  12. win环境的radius服务器搭建
  13. mysql drop表明_MySQL DROP TABLE会完全删除表还是仅删除结构?
  14. 苹果mac笔记本部分按键失灵,为什么要5连按⌥ option键
  15. android标题栏 状态栏,android设置无标题栏 、 状态栏
  16. 【MM小贴士】特殊采购类型40
  17. Android Camera(13)HEIF Imaging
  18. 一个数如果恰好等于它的因子(因子:即能够整除的数)之和,这个数就称为“完数”。 例如 6=1+2+3 28=1+2+4+7+14 编程找出10000以内的所有完数。
  19. ly-tab第三方组件使用记录
  20. 51单片机真的过时了吗?单片机、ARM、DSP、FPGA/CPLD

热门文章

  1. foxmail的邮局和端口_Foxmail的服务器设置
  2. 国内很好的IT学习网站
  3. linux shift命令,Linux中的shift命令
  4. linux tomcat创建文件夹乱码(显示:???)
  5. 耍耍Windows Live Writer
  6. 魅蓝E 刷android,魅蓝e怎么root,或者怎么刷成android系统
  7. swift 字符串截取
  8. 倒计时 2 天 | 年前不学习,年后无加薪!区块链开发者们不要纠结了!(内含赠票福利)...
  9. js DateFormat
  10. 安装mysql25步骤_通过yum安装MySQL_5.7.25详细过程