【Option】source_filtering
作用
用于定制返回的 document 里的 _source 字段包含的记录。正常来说,我们存的document的json长什么样子,通过 API-GET 获取到的 document 就是什么样子。我们发送的数据是保存在document里的_source字段,该字段里会包含记录的所有数据(就好比使用 SELECT * 与 SELECT <column1> 、<column2> ... 的区别),我们知道 数据的大小 会影响 IO性能 ,所以就有了这个功能的存在。
如何使用
想要实现这个功能有多种粒度:
?source=trueor?source=false,true表示查询的document需要包含_source字段(这个参数是默认);false表示查询的document中不包含_source字段。?_source_includes=<column1>,<column2>[,<column3>...],这里的column1、<column2>是返回的document中的_source要包含的字段。?_source_exclud=<column1>,<column2>[,<column3>...],这里的column1、<column2>是返回的document中的_source不要包含的字段。?_source=<column1>,<column2>[,<column3>...],这里的column1、column2是返回的document中的_source要包含的字段。功能同**_source_includes**。
【Option】routing
如何使用routing
这个参数可以填写任意值(是数值而不是文档里的字段),ES会通过一个 **hash()** 算法获取一个 整数值,这个 整数值指示了 **document** 应该存储在第几个主分片上,这就和 Redis的分片 一样样的:Redis 通过对 Key 进行哈希来确定这个 Key-Value 存在哪个分片里。
作用
我们想要某一类文档都存储在某一个固定的分片上,就能通过指定 ?routing=<固定值> 来保证增删改查发生在同一个固定分片上。
原理分析
- 客户端新增了一个
document并指定了?routing=abc Elasticsearch会先hash("abc")获取一个整数值,其哈希算法如下所示。
store_primary_shard_pos = hash("<any value>") % number_of_primary_shards
- 这里假设为哈希结果为 2,所以
Elasticsearch将新增的document存储在第 2 个主分片上。如果不指定
?routing,那么Elastichsearch默认拿document的_id作为hash()的值。
这里间接的说明了为什么Elastichsearch指定了 主分片数量后就不能再修改 的原因:这会导致原来的 ?routing="abc" 的结果失效。就拿上面的哈希算法来说明,就可以明白。
【Option】refresh
关于
refresh和realtime,建议先去看《ES 分片、refresh、落盘原理》。
可选参数值
true:立即、强制刷新与当前文档相关的主从分片,在使用这个选项时务必谨慎,因为会占用大量的资源(主要出现在IO上)wait_for:该选项 不会强制刷新主从分片 而是 会一直等待请求的文档记录可见,换句话说就是等待所需的文档被refresh到磁盘上。触发refresh的时机大概有以下几种:index.refresh_interval,该值在 创建索引 时指定的,默认值为1s,即每秒refresh一下索引。- 调用
refresh接口 - 执行设置了
?refresh=true的API,比如GET、UPDATE等等
false(默认):不执行任何操作。该选项仅保证 该文档记录 在请求结束后的某个时间点上 可见。最佳实践:如果被索引项对 一致性 要求不算特别高,尽量使用
refresh=false,并用realtime来解决实时性问题。
如何选择refresh
- 在仅考虑
refresh的情况下,如果更改的数据比较多,可以使用wait_for,因为index.refresh_interval,每一秒都会执行一次刷新,只要在这个间隔中更改的数据多一些,那么刷新一次带来的收益就更大些。但是如果一秒钟仅一次修改,使用wait_for也不会比refresh好到哪里去。 - 不要构建多个
?refresh=wait_for的请求,而是用一个?refresh=wait_for的批请求来包裹多个数据。这样做应该是为了减少客户端的线程等待把? - 如果在创建
index的时候设置了index.refresh_interval=-1,那么所有的?refresh=wait_for请求很有可能无限期的等待,直到发生了reresh操作 - 如果在创建
index的时候设置的index.refresh_interval的值比较小,那么仍然会构建需要tiny segment ?refresh=wait_for只影响当前请求,而?refresh=true会影响整个ES的进行为什么
?refresh=true的速度很慢?主要有三个原因:- 每次进行
refresh都会 创建一个**tiny segment**,这个tiny segment需要存储在磁盘上 - 在并发较高的情况下,比如 一秒钟进来 1000 个
?refresh=true的更新请求,那么就会创建 1000 个tiny segment,而且搜索时得进行1000次tiny segment的搜索 - 把
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
关于
refresh和realtime,建议先去看《ES 分片、refresh、落盘原理》。
现象
初次接触这个参数,觉得它和 refresh 很像, 似乎都涉及到了落盘的问题。实践之后发现它算是 refresh 的一种折中方案。我们先来看一组现象:
删除product
DELETE /product
创建一个
product索引,这里设置自动落盘间隔为 一分钟:PUT /product{"settings": {"refresh_interval": "60s"}}
创建几个文档记录
PUT /product/_doc/1{"name" : "xiaomi phone","desc" : "shouji zhong de zhandouji","price" : 3999,"tags": [ "xingjiabi", "fashao", "buka" ]}
PUT /product/_doc/2{"name" : "xiaomi nfc phone","desc" : "zhichi quangongneng nfc,shouji zhong de jianjiji","price" : 4999,"tags": [ "xingjiabi", "fashao", "gongjiaoka" ]}
PUT /product/_doc/3{"name" : "nfc phone","desc" : "shouji zhong de hongzhaji","price" : 2999,"tags": [ "xingjiabi", "fashao", "menjinka" ]}
查询此时的落库情况(一般情况下没这么快落库,所以会看到
product的索引的磁盘占用为几个字节,接近于 0 ):GET /_cat/shards?v
用 非实时的
GET查询记录1,正常情况下会是未找到:GET /product/_doc/1?realtime=false
用 实时的
GET查询记录1,会发现能够查询到数据了:GET /product/_doc/1?realtime=false
若此时继续用 非实时的
GET查询记录1,那么依然会是未找到的情况:GET /product/_doc/1?realtime=false
理解
看了一波 Elasticsearch 的源码,在 realtime get 的方法里面有几行关于 更新事务日志的查询 的代码,也就是说在 realtime 下可能会从内存中的某条事务记录里提取到 文档更新的值,这种情况大大提高了及时性,但是在宕机等异常情况下可能会产生数据不一致的问题(看到的仍然有可能再丢失)。相较而言,refresh 牺牲了一部分的资源 来保证数据的一致性(看到的必然不会再丢失)。
作用
是否能够直接从Buffer中 或者 translog 中获取数据(具体参考《ES 分片、refresh、落盘原理》)。
