【Option】source_filtering

作用

用于定制返回的 document 里的 _source 字段包含的记录。正常来说,我们存的documentjson长什么样子,通过 API-GET 获取到的 document 就是什么样子。我们发送的数据是保存在document里的_source字段,该字段里会包含记录的所有数据(就好比使用 SELECT *SELECT <column1> 、<column2> ... 的区别),我们知道 数据的大小 会影响 IO性能 ,所以就有了这个功能的存在。

如何使用

想要实现这个功能有多种粒度:

  1. ?source=trueor ?source=falsetrue 表示查询的document需要包含 _source 字段(这个参数是默认);false 表示查询的document中不包含_source字段。
  2. ?_source_includes=<column1>,<column2>[,<column3>...] ,这里的column1<column2> 是返回的 document 中的 _source 要包含的字段。
  3. ?_source_exclud=<column1>,<column2>[,<column3>...] ,这里的column1<column2> 是返回的document 中的_source 不要包含的字段。
  4. ?_source=<column1>,<column2>[,<column3>...],这里的column1column2 是返回的document中的_source要包含的字段。功能同**_source_includes**

【Option】routing

如何使用routing

这个参数可以填写任意值(是数值而不是文档里的字段)ES会通过一个 **hash()** 算法获取一个 整数值,这个 整数值指示了 **document** 应该存储在第几个主分片上,这就和 Redis的分片 一样样的:Redis 通过对 Key 进行哈希来确定这个 Key-Value 存在哪个分片里。

作用

我们想要某一类文档都存储在某一个固定的分片上,就能通过指定 ?routing=<固定值> 来保证增删改查发生在同一个固定分片上。

原理分析

  1. 客户端新增了一个 document 并指定了?routing=abc
  2. Elasticsearch会先hash("abc")获取一个整数值,其哈希算法如下所示。

store_primary_shard_pos = hash("<any value>") % number_of_primary_shards

  1. 这里假设为哈希结果为 2,所以 Elasticsearch 将新增的document存储在第 2 个主分片上。

    如果不指定 ?routing,那么 Elastichsearch 默认拿 document_id 作为 hash() 的值。

这里间接的说明了为什么Elastichsearch指定了 主分片数量后就不能再修改 的原因:这会导致原来的 ?routing="abc" 的结果失效。就拿上面的哈希算法来说明,就可以明白。

【Option】refresh

关于refreshrealtime,建议先去看《ES 分片、refresh、落盘原理》

可选参数值

  • true:立即、强制刷新与当前文档相关主从分片,在使用这个选项时务必谨慎,因为会占用大量的资源(主要出现在IO上)
  • wait_for:该选项 不会强制刷新主从分片 而是 会一直等待请求的文档记录可见,换句话说就是等待所需的文档被refresh到磁盘上。触发refresh的时机大概有以下几种:
    • index.refresh_interval,该值在 创建索引 时指定的,默认值为 1s,即每秒refresh一下索引。
    • 调用 refresh 接口
    • 执行设置了?refresh=true的API,比如GETUPDATE等等
  • false(默认):不执行任何操作。该选项仅保证 该文档记录请求结束后的某个时间点可见

    最佳实践:如果被索引项对 一致性 要求不算特别高,尽量使用 refresh=false,并用 realtime 来解决实时性问题

如何选择refresh

  1. 在仅考虑refresh的情况下,如果更改的数据比较多,可以使用wait_for,因为index.refresh_interval,每一秒都会执行一次刷新,只要在这个间隔中更改的数据多一些,那么刷新一次带来的收益就更大些。但是如果一秒钟仅一次修改,使用wait_for也不会比refresh好到哪里去。
  2. 不要构建多个?refresh=wait_for的请求,而是用一个?refresh=wait_for的批请求来包裹多个数据。这样做应该是为了减少客户端的线程等待把?
  3. 如果在创建index的时候设置了index.refresh_interval=-1,那么所有的?refresh=wait_for请求很有可能无限期的等待,直到发生了reresh操作
  4. 如果在创建index的时候设置的index.refresh_interval的值比较小,那么仍然会构建需要 tiny segment
  5. ?refresh=wait_for只影响当前请求,而?refresh=true会影响整个ES的进行

    为什么?refresh=true的速度很慢?主要有三个原因:

    1. 每次进行refresh都会 创建一个 **tiny segment**,这个tiny segment需要存储在磁盘
    2. 在并发较高的情况下,比如 一秒钟进来 1000 个?refresh=true的更新请求,那么就会创建 1000 个tiny segment,而且搜索时得进行1000次 tiny segment 的搜索
    3. tiny segment 合并为 large segment

关于?refresh=wait_for的注意事项:ES里面有一个index.max_refresh_listeners参数( 默认值为:1000 ),该参数指定了 该索引所在的某个分片 上允许等待的?refresh=wait_for的请求数量。 如果某一个分片里已经有 1000 个?refresh=wait_for的请求正在等待中,那么新来的?refresh=wait_for请求将会触发强制刷新,然后 1001个?refresh=wait_for全都返回。

作用

决定数据是否落盘。

【Option】realtime

关于refreshrealtime,建议先去看《ES 分片、refresh、落盘原理》

现象

初次接触这个参数,觉得它和 refresh 很像, 似乎都涉及到了落盘的问题。实践之后发现它算是 refresh 的一种折中方案。我们先来看一组现象:

  1. 删除product

    1. DELETE /product
  2. 创建一个 product 索引,这里设置自动落盘间隔为 一分钟:

    1. PUT /product
    2. {
    3. "settings": {
    4. "refresh_interval": "60s"
    5. }
    6. }
  3. 创建几个文档记录

    1. PUT /product/_doc/1
    2. {
    3. "name" : "xiaomi phone",
    4. "desc" : "shouji zhong de zhandouji",
    5. "price" : 3999,
    6. "tags": [ "xingjiabi", "fashao", "buka" ]
    7. }
    1. PUT /product/_doc/2
    2. {
    3. "name" : "xiaomi nfc phone",
    4. "desc" : "zhichi quangongneng nfc,shouji zhong de jianjiji",
    5. "price" : 4999,
    6. "tags": [ "xingjiabi", "fashao", "gongjiaoka" ]
    7. }
    1. PUT /product/_doc/3
    2. {
    3. "name" : "nfc phone",
    4. "desc" : "shouji zhong de hongzhaji",
    5. "price" : 2999,
    6. "tags": [ "xingjiabi", "fashao", "menjinka" ]
    7. }
  4. 查询此时的落库情况(一般情况下没这么快落库,所以会看到 product 的索引的磁盘占用为几个字节,接近于 0 ):

    1. GET /_cat/shards?v
  5. 用 非实时的GET 查询记录1,正常情况下会是未找到:

    1. GET /product/_doc/1?realtime=false
  6. 用 实时的 GET 查询记录1,会发现能够查询到数据了:

    1. GET /product/_doc/1?realtime=false
  7. 若此时继续用 非实时的 GET 查询记录1,那么依然会是未找到的情况:

    1. GET /product/_doc/1?realtime=false

理解

看了一波 Elasticsearch 的源码,在 realtime get 的方法里面有几行关于 更新事务日志的查询 的代码也就是说在 realtime 下可能会从内存中的某条事务记录里提取到 文档更新的值,这种情况大大提高了及时性,但是在宕机等异常情况下可能会产生数据不一致的问题(看到的仍然有可能再丢失)。相较而言,refresh 牺牲了一部分的资源 来保证数据的一致性(看到的必然不会再丢失)。


作用

是否能够直接从Buffer中 或者 translog 中获取数据(具体参考《ES 分片、refresh、落盘原理》)。