文章目录
- 1. 简介
- 2. index常规设置
- 3. index的各个功能模块
- 1. analysis相关的特性设置
- 2. index shard allocation 相关的设置
- 3. maping 设置
- 4. merging 设置,控制shard的background merge 进程的工作方式
- 5. similarities 设置,自定义相似度计算模型来计算search的result的score
- 6. slowlog 设置,控制记录慢查询
- 7. store 设置,设置shard data存储的文件系统的类型
- 8. tranlog , 控制transaction log和background flush 操作。
- 9. index-sorting
1. 简介
index modules 主要是介绍index各个方面的特性,算是一些总结和补充
主要内容有以下几方面
- 常见的index settings: 每个index在index level的设置,有static和dynamic之分,这些属性相对来说比较碎一些,相对来说不牵扯另一个完整的功能模块,而且有些直接是index自己的属性(比如name,shard数量等),所以放在这里
- index的各个功能特性
- analysis相关的特性设置
- index shard allocation 相关的设置
- maping 设置
- merging 设置,控制shard的background merge 进程的工作方式
- similarities 设置,自定义相似度计算模型来计算search的result的score
- slowlog 设置,控制记录慢查询
- store 设置,设置shard data存储的文件系统的类型
- tranlog , 控制transaction log和background flush 操作。
- x-pack index想过的设置
- index lifecycle management 索引生命周期管理
2. index常规设置
index的常规设置一般分为两类,一类是静态的static,这一类一般都是在index create的时候指定,之后不能再进行修改(或者在index close之后才能修改),
还有一类设置被称为动态设置dynaic setting, 这一类的设置一般可以通过api进行直接的修改。
1. static setting
static的settin总共有以下相关的设置
- index.number_of_shards: index的primary shard 数量设置
- index.shard.check_on_startup: 在es启动的时候对shard做哪些检查
- index.codec: index数据存储的编码方式(压缩方式)
- index.routing_partition_size: 这个是自定义routing的时候可以做的一些设置
- index.load_fixed_bitset_filters_eagerly: 对于nested query 是否对filter操作做预加载
1. index.number_of_shards
index的primary shard 数量设置, 默认为1, 即使在closed index上也不能修改,每个index的shard被限制到1024个,
这样做是为了防止一些意外创建的index(shard过多)占用比较多的资源导致cluster不稳定。
这个最大值可以通过java options进行设置,比如
export ES_JAVA_OPTS="-Des.index.max_number_of_shards=128"
2. index.shard.check_on_startup
在es启动的时候对shard做哪些检查
false: (default) 不对shard的完整性做检查
checksum: 只校验物理存储上的完整性
true: 不仅会校验物理存储上的完整性,还会校验逻辑存储上的完整性,这对cpu和memory来讲可能是一个非常昂贵的开销,也有可能会消耗很多时间。
3. index.codec
index数据存储的编码方式(压缩方式)
默认使用LZ4压缩方式,可以设置
"index.codec" : "best_compression"
来开启使用压缩率更好的算法DEFLATE,当担这个算法对cpu的消耗更大,所以也会再一定程度上影响写入的速度。
如果你更新了这个设置,新的存储凡是会在segments merge之后生效。
4. index.routing_partition_size: 这个是自定义routing的时候可以做的一些设置
这个设置的作用不是很大,可以参考index的meta field _routing来理解
这个字段的值理论上小于index.number_of_shards(只有index.number_of_shards=1的时候可以相等),默认值为1
5. index.load_fixed_bitset_filters_eagerly
对于nested query 是否对filter操作做预加载,这个可能看了nested filter之后能够更近异步理解
index.load_fixed_bitset_filters_eagerly
Indicates whether cached filters are pre-loaded for nested queries. Possible values are true (default) and false.
2. dynamic setting
动态的设置是指那些可以通过api动态修改的设置,相对static设置莱多,dynaimc 设置更多一些。
- index.number_of_replicas: 副本replica,默认为1
- index.auto_expand_replicas: 副本数基于cluster中的node数进行变化,可以设置一个上限或者下限,默认是false, 这种分配方式不是很好
- index.search.idle.after: 分片在多久时间内没有接收搜索请求会被认为是idle的shard (默认为30秒)。
- index.refresh_interval: 多久执行一次refresh操作,refresh操作让这些changes能够被后面的搜索搜到。默认是1s,可以通过设置为-1来禁用refresh,如果这一项没有做显示的设计的话,如果一个shard在
index.search.idle.after
时间内没有接收到search请求的话,那么这个shard不会再进行background的refresh 知道搜到search请求之后才会进行refresh,对应的search请求也会等到执行完refresh之后才会执行。这种设置主要是为了又会bulk index操作。如果不想执行这个默认的逻辑,可以通过显示的设置这个属性为1s。 - index.max_result_window: 翻页搜索的总记录条数from+size的总数,默认不超过10000,防止占用过高的memory,如果想进行神翻翻页的话尝试使用scroll 或者 search after api。
- index.max_inner_result_window: 这个和上一个类似,主要设置的是返回结果中的结果,和agg的top数,默认是100.
- index.max_rescore_window: 在搜索此索引时,rescore请求的window_size的最大值。默认为index.max_result_window,即10000。搜索请求占用堆内存和时间与max(window_size,from + size)成比例,这限制了内存的使用
- index.max_docvalue_fields_search: 查询中允许的最大docvalue_field数,默认为100,Doc-value 字段成本很高,因为它们可能会导致每个字段的每个文档搜索。
- index.max_script_fields: 查询中允许的最大script_field数。默认为32。
- index.max_ngram_diff: NGramTokenizer和NGramTokenFilter的min_gram和max_gram之间允许的最大差异。默认为1。
- index.max_shingle_diff: ShingleTokenFilter的max_shingle_size和min_shingle_size之间允许的最大差异。默认为3。
- index.blocks.read_only: 设置为true以使索引和索引元数据只读,false设置为允许写入和元数据更改。感觉这个block和下面的几个相反,好奇怪。
- index.blocks.read_only_allow_delete: 与index.blocks.read_only相同,但允许删除索引以释放资源。
- index.blocks.read: 设置为true以禁用对索引的读取操作。
- index.blocks.write: 设置为true以禁用对索引的数据写入操作。与read_only不同,此设置不会影响元数据。例如,您可以使用write_block 关闭索引,但不能使用read_only_block 关闭索引。
- index.blocks.metadata: 设置为true以禁用索引元数据读取和写入。
- index.max_refresh_listeners: 索引的每个分片上可用的最大刷新侦听器数。 自定义实现了refresh=wait_for的listener
- index.analyze.max_token_count: _analyze API能够产生的token的最大数量,默认是10000.
- index.highlight.max_analyzed_offset: 高亮显示的最大字符数,这个设置仅仅针对那些indexing的时候不保存term的offset 且没有保存term vectors的feild.
- index.max_terms_count: Term查询中可以使用的最大Term数。默认为65536。
- index.max_regex_length: 正则查询中正则表达式的最大长度,默认1000.
- index.routing.allocation.enable: 控制此索引的分片分配。all: 默认,允许所有分片分配; primaries:只允许主片分配;new_primaries:只允许新创建的primaries shard分配; none: 不允许任何分片分配
- index.routing.rebalance.enable: 为此索引启用分片重新平衡, all: 默认,允许分片缺失等问题发生的时候进行重新平衡,primaries: 只允许primaries进行再平衡;replicas: 只允许replicas进行再平衡;none: 不允许进行再平衡,需要进行实例测试。
- index.gc_deletes: 允许已删除文档的版本号,仍可用于进一步版本化操作的时间长度。默认60s
- index.default_pipeline: 设置默认的pipeline,默认是没有的
- index.final_pipeline: 最后的pipeling
3. index的各个功能模块
index相关的其他的module级别的特性有
- analysis相关的特性设置
- index shard allocation 相关的设置
- maping 设置
- merging 设置,控制shard的background merge 进程的工作方式
- similarities 设置,自定义相似度计算模型来计算search的result的score
- slowlog 设置,控制记录慢查询
- store 设置,设置shard data存储的文件系统的类型
- tranlog , 控制transaction log和background flush 操作。
1. analysis相关的特性设置
这个不再赘述,前面有相关的文档介绍了anlysis
主要是定义 analyzers, tokenizers, token filters and character filters.
2. index shard allocation 相关的设置
shard allocation主要控制了node上的shard分配相关的规则,他有一下能力
- Shard allocation filtering: 可以控制某个shard分配到某个node
- Delayed allocation: 在因为一个node离开cluster的时候会造成unassigned shard,这个设置可以控制这些unassigned的shard 延迟分配。
- Total shards per node: 一个index在一个node上面最多可以有多少个shard
1. Shard allocation filtering: 可以控制某个shard分配到某个node
你可以使用shard allocation filters 来控制index的shard会被分配到哪些node上面。这个针对每个index的filters会与cluster范围内的allocation filter和 allocation awareness 配合使用。
shard allocation filters 可与基于node attribute, built-in _name, host_ip, publish_ip, _ip 和_host attributes进行过滤。 Index lifecycle management使用filters来决定如何如何对shard进行重分配。
shard allocation filter 的主要配置是 cluster.routing.allocation
这个设置是dynamic的,可以使live index的shard从当前的node上面迁移到别的上面。当然这些迁移不能打破其他的约束,比如不能吧primary和replica shard 放到同一个node上面。
比如说,你可以自定义一些node attribute 来指明不同node的性能特性,然后使用使用shard allocation filtering 来将不同的shard route到具有不同的硬件特性的node当中,这里适用的一个场景就是日志系统的冷热分离,如果是按天产生的索引,可以把索引进行冷热分离,集群中的机器分为两种,cold和hot, cold是大磁盘,可能直接使用机械盘,成本低,适合低写入打存储,提取速度要求不高的场景,hot是高性能磁盘,一般为ssd,但是磁盘的容量偏小,适合高写入的场景,可以将昨天的日志index迁移到cold的node,今天新产生的分配到hot node 来满足大量的写需求,同时又能满足大存储的需求,可以降低很多成本。
1. 使用shard allocation filter的一般方式
1.给对应的node设置attribute,假如我们为每个node标记一个容量size属性,有small,medium,big三个属性,
node.attr.size: medium
或者
./bin/elasticsearch -Enode.attr.size=medium
- 为对应的索引增加routing allocations filter
PUT test/_settings
{
"index.routing.allocation.include.size": "big,medium"
}
2. index.routing.allocation可以有的设置
index.routing.allocation.include.{attribute}
只需要node的attribute中有一个在当前index的配置当中即可
index.routing.allocation.require.{attribute}
对应的node必须有全部的当前配置的attribute才会将分片分配上去
index.routing.allocation.exclude.{attribute}
对应的node没有任何当前配置的的attribute才会将分片分配上去
PUT test/_settings
{
"index.routing.allocation.include.size": "big",
"index.routing.allocation.include.rack": "rack1"
}
这个配置就会将test index 移动到rack位rack1, size为big的node上面
3. 支持的built-in attribute
_name: Match nodes by node name
_host_ip: Match nodes by host IP address (IP associated with hostname)
_publish_ip: Match nodes by publish IP address
_ip: Match either _host_ip or _publish_ip
_host: Match nodes by hostname
PUT test/_settings
{
"index.routing.allocation.include._ip": "192.168.2.*"
}
感觉这个不常用,因为可能会变化,机器做了下线重新部署ip就变了,_name就有唯一性,不容易聚类
2. Delayed allocation
在因为一个node离开cluster的时候会造成unassigned shard,这个设置可以控制这些unassigned的shard 延迟分配。
1. node left之后master会做的操作
在一个nodeA离开cluster后,正常情况下master会做下面这些操作
- 将cluster中其他的node拥有的nodeA中的primary shard 对应的replica 提升为primary(因为nodeA不在了,所有的primary也就丢了)
- 在node够用的情况下从新allocating 那些丢失了的 replica shar
- 在剩下的node中进行rabalance,以保证shard在集群中均匀分配
这些行为都是为了让集群能够避免数据丢失而且是能够更加快速的被备份。即使es对cluster层面和node层面能够并行恢复的shard数量,但是他还是会对cluster带来挺大的额外的load,如果一node突然挂了,然后又很快(几分钟)又恢复了并重新加入了集群,急着进行shard recovery似乎是不划算的。
想象一下下面的场景
- node5 和集群中的其他节点发生了网络隔离,被认为died
- master 提升了哪些node5上的primary shard 对应的其他node上的replica为primary shard
- master 在cluster上面的其他节点上为丢失的replica allocate shard
- 每个replica再从primary copy数据(通过network)
- reblace,shard在cluster中的不同node之间迁移,来让集群变得更加平衡
- node5在几分钟后又回来了
- master再次进行rebalance以便于把一些shards分配到node5上面
在这个场景下,如果master在做完第二步之后啥都不做,等待个几分钟,在node5回来之后,丢失的shards会re-allocated到node5上面,但是这样的话需要通过网络拷贝的数据量大大减小
对于那些已经auto sync-flushed 的idle shard(没有进行index 操作),恢复会更快。
2. 延迟分配的设置
index的延迟分配可以通过 index.unassigned.node_left.delayed_timeout
参数进行设置
PUT _all/_settings
{
"settings": {
"index.unassigned.node_left.delayed_timeout": "5m"
}
}
如果进行了延迟分配设置,上面的情况就会变成这样
- node5 和集群中的其他节点发生了网络隔离,被认为died
- master 提升了哪些node5上的primary shard 对应的其他node上的replica为primary shard
- master 输出一条延迟分配的日志,记录那些unassigned shards 以及delay time
- cluster 保持yellow状态(因为有unassigned shards)
- node5 在timeout 之前重新加入了集群
- node5在几分钟后又回来了
- 丢失的replica 被重新分配到node5(如果是sync-flushed shards 那么会立刻恢复)
这个设置仅仅会对因为node丢失导致的shard missing起作用,对新索引创建等其他情况产生的没有影响。
在整个集群重启之后,如果重启前有node left导致的shard missing那么重启后会进行恢复
在master 失败的情况下,已经经过的dely time会丢失,然后重新计算
3. 延迟分配的触发后的取消
如果延迟的时间到了,就会进行shard的recovery.如果这个时间missing node又re-join 到cluster当中了,而且他的shard 仍然和primary shard有相同的sync-id, shard relocation会被cancelled,然后原来的那个shard被用来做recovery,所以,es将默认的delay time设置为 1 minute。
如果你要将一个node永久移除,直接将延迟设置为0即可
PUT _all/_settings
{
"settings": {
"index.unassigned.node_left.delayed_timeout": "0"
}
}
然后再missing shards 开始进行recover的时候需要将这个值重新设置回来。
4. 查看unassigned shards
有时候索引非常多,不容易发现到底是哪个index的哪个shard异常了,可以通过health API查看
GET _cluster/health
5. recovery的顺序
- the optional index.priority setting (higher before lower)
- the index creation date (higher before lower)
- the index name (higher before lower)
这说明默认情况下新新建的index比旧的的index先进行recovery
3. Total shards per node: 一个index在一个node上面最多可以有多少个shard
index.routing.allocation.total_shards_per_node
对于一个index来说,最多有多少个shards分配到单个node上面
cluster.routing.allocation.total_shards_per_node
对于集群范围来说,单个node的shards总数最大值
这两个设置都要谨慎使用,使用不当容易出错。
3. maping 设置
这个也不再赘述,前面已经阐述很多了
4. merging 设置,控制shard的background merge 进程的工作方式
每一个shard都是一个lucene index, 每个lucene又由很多个segments构成,segement是数据存储的基本单元,后台会周期性的将small segment 合并为更大的segment,同时也会将delete segment删除的文档去掉。
这个过程会自动根据当前的硬件资源使用的情况进行限速throttling ,比如会考虑当前search的压力
使用的是ConcurrentMergeScheduler 来实现以上行为。merges是通过多个独立的线程来进行,ConcurrentMergeScheduler可以使用的最大线程数是可以设置的。
index.merge.scheduler.max_thread_count: 设置最大可以使用的线程数
如果没有设置的话,默认使用
Math.max(1, Math.min(4, Runtime.getRuntime().availableProcessors() / 2))
这个计算方式对于ssd来说工作的很不错,如果你的是机械转盘,可以把这个降低到1更好。
5. similarities 设置,自定义相似度计算模型来计算search的result的score
similarity 也被称为scoring/ranking dodel, 主要是定义了doc如何被打分。每个field都可以定义自己的similarity。
自定义similarity可以认为是一个专家级操作,正常情况下es的built-in similarities应该就足够了。
built-in similarities有
- BM25 similarity
- DFR similarity
- DFI similarity
- IB similarity
- LM Dirichlet similarity
- LM Jelinek Mercer similarity
- Scripted similarity
在配置的时候需要注意的是,如果filed没有特殊指出使用哪个similarity es会使用名字为 default
的similarity
1. 自定义similarity的方式
可以在创建index的时候定一个similarity
PUT /index
{
"settings" : {
"index" : {
"similarity" : {
"my_similarity" : {
"type" : "DFR",
"basic_model" : "g",
"after_effect" : "l",
"normalization" : "h2",
"normalization.h2.c" : "3.0"
}
}
}
}
}
PUT /index/_mapping
{
"properties" : {
"title" : { "type" : "text", "similarity" : "my_similarity" }
}
}
2. built-in similarities
1. BM25 similarity
可以有的配置
k1: 默认值1.2, 非线性的term frequency 归一化参数
b: 默认值0.75,doc length 归一化参数
discount_overlaps:
2. DFR similarity
3. DFI similarity
4. IB similarity
5. LM Dirichlet similarity
6. LM Jelinek Mercer similarity
7. Scripted similarity
6. slowlog 设置,控制记录慢查询
这个就像mysql的slow log一样,可以记录慢查询/写入
- slow search
- slow index
1. slow search
可以设置query和fetch两个阶段的慢日志
PUT /twitter/_settings
{
"index.search.slowlog.threshold.query.warn": "10s",
"index.search.slowlog.threshold.query.info": "5s",
"index.search.slowlog.threshold.query.debug": "2s",
"index.search.slowlog.threshold.query.trace": "500ms",
"index.search.slowlog.threshold.fetch.warn": "1s",
"index.search.slowlog.threshold.fetch.info": "800ms",
"index.search.slowlog.threshold.fetch.debug": "500ms",
"index.search.slowlog.threshold.fetch.trace": "200ms",
"index.search.slowlog.level": "info"
}
使用不同的日志等级是为了方便更快的进行grep操作。
对应的log4j2.properties配置为
appender.index_search_slowlog_rolling.type = RollingFile
appender.index_search_slowlog_rolling.name = index_search_slowlog_rolling
appender.index_search_slowlog_rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_index_search_slowlog.log
appender.index_search_slowlog_rolling.layout.type = PatternLayout
appender.index_search_slowlog_rolling.layout.pattern = [%d{ISO8601}][%-5p][%-25c] [%node_name]%marker %.-10000m%n
appender.index_search_slowlog_rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_index_search_slowlog-%i.log.gz
appender.index_search_slowlog_rolling.policies.type = Policies
appender.index_search_slowlog_rolling.policies.size.type = SizeBasedTriggeringPolicy
appender.index_search_slowlog_rolling.policies.size.size = 1GB
appender.index_search_slowlog_rolling.strategy.type = DefaultRolloverStrategy
appender.index_search_slowlog_rolling.strategy.max = 4
logger.index_search_slowlog_rolling.name = index.search.slowlog
logger.index_search_slowlog_rolling.level = trace
logger.index_search_slowlog_rolling.appenderRef.index_search_slowlog_rolling.ref = index_search_slowlog_rolling
logger.index_search_slowlog_rolling.additivity = false
2. slow index
PUT /twitter/_settings
{
"index.indexing.slowlog.threshold.index.warn": "10s",
"index.indexing.slowlog.threshold.index.info": "5s",
"index.indexing.slowlog.threshold.index.debug": "2s",
"index.indexing.slowlog.threshold.index.trace": "500ms",
"index.indexing.slowlog.level": "info",
"index.indexing.slowlog.source": "1000"
}
log4j2.properties配置
appender.index_indexing_slowlog_rolling.type = RollingFile
appender.index_indexing_slowlog_rolling.name = index_indexing_slowlog_rolling
appender.index_indexing_slowlog_rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_index_indexing_slowlog.log
appender.index_indexing_slowlog_rolling.layout.type = PatternLayout
appender.index_indexing_slowlog_rolling.layout.pattern = [%d{ISO8601}][%-5p][%-25c] [%node_name]%marker %.-10000m%n
appender.index_indexing_slowlog_rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_index_indexing_slowlog-%i.log.gz
appender.index_indexing_slowlog_rolling.policies.type = Policies
appender.index_indexing_slowlog_rolling.policies.size.type = SizeBasedTriggeringPolicy
appender.index_indexing_slowlog_rolling.policies.size.size = 1GB
appender.index_indexing_slowlog_rolling.strategy.type = DefaultRolloverStrategy
appender.index_indexing_slowlog_rolling.strategy.max = 4
logger.index_indexing_slowlog.name = index.indexing.slowlog.index
logger.index_indexing_slowlog.level = trace
logger.index_indexing_slowlog.appenderRef.index_indexing_slowlog_rolling.ref = index_indexing_slowlog_rolling
logger.index_indexing_slowlog.additivity = false
7. store 设置,设置shard data存储的文件系统的类型
1. store的类型和设置
这个设置主要是针对文件系统的一些设置,物理机的文件系统是各种各样的,默认情况下es会根据操作系统的实际情况选择最佳读写方式,你也可以对这个进行设置。
这个设置是static的,而且这个也是一个专家级设置,有可能后面会移除这个设置
这个设置可以直接在elasticsearch.yml中设置全局的
index.store.type: niofs
也可以针对单个的index设置
PUT /my_index
{
"settings": {
"index.store.type": "niofs"
}
}
可以有以下设置:
-
fs: 默认文件系统实现。这将根据操作环境选择最佳的实现方式,当前的操作环境在所有受支持的系统上都是hybridfs,但可能会发生变化。
-
simplefs: Simple FS类型是使用随机访问文件直接实现文件系统存储(映射到Lucene SimpleFsDirectory)。此实现的并行性能较差(多个线程将成为瓶颈)。当您需要索引持久性时,通常最好使用niofs。
-
niofs: NIO FS类型使用NIO在文件系统上存储分片索引(映射到Lucene NIOFSDirectory)。它允许多个线程同时读取同一文件。由于SUN Java实现中存在bug,因此不建议在Windows上使用。
-
mmapfs: MMap FS类型通过将文件映射到内存(mmap)将碎片索引存储在文件系统上(映射到Lucene MMapDirectory)。内存映射会占用进程中虚拟内存地址空间的一部分,该空间等于要映射的文件的大小。在使用此类之前,请确保您已允许足够的虚拟地址空间。
-
hybridfs: hybridfs类型是niofs和mmapfs的混合类型,它根据读取访问模式为每种文件类型选择最佳的文件系统类型。当前,只有Lucene term 词典,norms 和doc values 文件才进行内存映射(mmap)。使用Lucene NIOFSDirectory打开所有其他文件。与mmapfs相似,请确保已允许大量虚拟地址空间。
如果你没有权限使用大量的memory maps 你可以通过node.store.allow_mmap 来设置,这个是一个boolean值。默认是true,你可以设置为false。
2. 预加载数据导文件系统的缓存中
这个也是专家级设置,将来有可能修改。
默认情况下,Elasticsearch完全依靠操作系统文件系统缓存来缓存I / O操作。可以通过设置index.store.preload,以告知操作系统在opening索引的时候将索引文件的内容加载到内存中。
此设置接受以逗号分隔的文件扩展名列表:扩展名在列表中的所有文件将在打开时预加载。这对提高索引的搜索性能很有用,尤其是在重新启动主机操作系统时,因为这会导致文件系统缓存被破坏。
但是请注意,这可能会减慢索引的打开速度,因为只有将数据加载到物理内存中后,索引才能变得可用。
这个设置可以直接在elasticsearch.yml中设置全局的
index.store.preload: ["nvd", "dvd"]
也可以针对单个的index设置
PUT /my_index
{
"settings": {
"index.store.preload": ["nvd", "dvd"]
}
}
里面的设置也支持wildcard的设置。
8. tranlog , 控制transaction log和background flush 操作。
这一部分就结合es的refresh,flush操作一起来理解好了。
1. es shard和lucene的关系
一个lucene 的shard(后面称es_shard)在lucene中对应了一个索引index(后面称lucene_index)
lucene_index 的构成是由多个segment构成的。
2. luncene_index满足事务特性,
- 当IndexReader.open打开一个索引的时候,相对于给当前索引进行了一次snapshot,此后的任何修改都不会被看到。
- 仅当IndexReader.open打开一个索引后,才有可能看到从上次打开后对索引的修改。
- 当IndexWriter没有调用Commit的时候,其修改的内容是不能够被看到的,哪怕IndexReader被重新打开。
- 欲使最新的修改被看到,一方面IndexWriter需要commit(产生新的segment),一方面IndexReader重新打开。
lucene commit :lucene commit针对的是lucene_index不是某一个segment,会应用新的curd , merge 一些segment产生新的luncene_index 的segment,并持久化到磁盘。
lucene reopen : 想要新的增改删可以应用到查询中,比如进行reopen才行。
也就是所es想要新的内容可见的话理论上必须有一个commit+reopen的操作。实际上上这样做是比较耗时的,在这里可以简略的理解为es的一个可能实现。
3. es如何应对增删改
es为了追求更好的近实时性,引入了tranlog。每一个增删改请求进来后会生成两份,一份是记录到translog当中,一份是记录到in-memory buffer当中。
当执行_refresh操作的时候(es默认每秒执行一次),in-memory-buffer 会被copy生成一个新的memory-segment,这个时候应该做了一些优化(实现了更快的类似commit+reopen)
这个memory-segment随后就是searchable的了。但是这个时候memory-segment并没有被持久化。这个时候如果服务崩了就可以通过translog来进行数据回放重建。translog可以设置为对Index, Bulk, Delete, or Update 在响应前都进行持久化,也可以设置为异步持久化(有丢数据风险)。
flush 对应的本质实际上是一个lucene的commit操作,他将memory-segment merge产生新的segment写到磁盘,同时创建新的translog文件(不会直接删除老的translog),这是一个相对昂贵的操作。
es没有删除translog主要是为了在replica从primary复制的时候为了加快复制速度有时候直接通过传输translog文件来加快recovery的过程。
4. translog的setting
index.translog.durability: 这个设置的是translog的durability(持久化)方式,默认的配置是request
,也就是意味着es对于index, delete, update, or bulk 请求,只有translog在primary和所有的replica上完成了持久化才会给client返回成功。他还可以被设置为 async
,这样的话就会对translog进行一步的fsyncs,时间是 index.translog.sync_interval
(默认to 5 seconds).
index.translog.sync_interval: 默认是5s,不能小于100ms
index.translog.flush_threshold_size: 进行flush操作的translog阈值,为了防止在shard recovery的时候通过大量的translog重建(相对较慢),会在translog达到一定的大小后进行lucene commit 操作,把translog中的内容应用到磁盘当中。
index.translog.retention.size: 这个设置的translog所有文件总的最大大小,保持多一些tranlog文件能够在replica恢复的时候直接通过网络拷贝primary的translog加快数据同步的过程。如果translog的比较低效,还是会走通过segment的文件进行同步。默认的大小是512mb,超过了之后会删除旧的文件。
index.translog.retention.age: tranlog文件最长保留时长,默认是12h.
这篇文章将es的refresh,flush,translog之间的关系讲的比较清楚
https://qbox.io/blog/refresh-flush-operations-elasticsearch-guide
https://www.jianshu.com/p/15837be98ffd
刘大佬的这篇文章可以作为一个注脚
https://www.cnblogs.com/forfuture1978/archive/2010/06/08/1753642.html
https://www.cnblogs.com/forfuture1978/archive/2010/06/27/1766162.html
luncene 的事务特性
https://www.cnblogs.com/forfuture1978/archive/2010/06/07/1752917.html
9. index-sorting
1. index-sorting简介
这个的作用是在lucene创建segment的时候指定文档的排列顺序,默认情况下lucene是按照index的先后直接排列的,没有固定的规则。
这个博客对index-sorting的特点介绍的很完整,
这篇lucene 也很好
index-sortring的功能主要就是在生成segment的时候使文档按照某个field排序后的值进行排列,这样的好处是doc_id的顺序和该field的顺序是一致的。在进行sort取topN的时候,只需要取每个segment的topN即可。
使用,下面是一个使用了多个字段排序的segment
PUT twitter
{
"settings" : {
"index" : {
"sort.field" : ["username", "date"],
"sort.order" : ["asc", "desc"]
}
},
"mappings": {
"properties": {
"username": {
"type": "keyword",
"doc_values": true
},
"date": {
"type": "date"
}
}
}
}
2. 可以有以下属性
index.sort.field: 是一个list,标识按照哪些fields进行排序
index.sort.order: 对每个field的排序规则,asc, desc
index.sort.mode: es可以使用multi-valued fields, 也就是说这个field的值有可能是一个array, 这个时候可以选择使用选择array中的哪一个参与排序,可以有min,max
两个选项,分别标识使用最小值和最大值。
index.sort.missing: 对于没有排序字段的doc如何处理,有两个选项_last, _first
放在最后一位或者第一位
index-sorting只能在index create的时候指定,不能再已经创建过的index上进行设置或者update.
3. index-soring的主要功能
可以提前结束查询过程,返回查询结果。
PUT events
{
"settings" : {
"index" : {
"sort.field" : "timestamp",
"sort.order" : "desc"
}
},
"mappings": {
"properties": {
"timestamp": {
"type": "date"
}
}
}
}
默认情况下如果没有设置index-sorting ,es的一个request会遍历所有query命中的doc,根据doc id取出来sorted field对应的doc_value 然后排序,再取前N条。但是假如设置了index-soring,同时,查询使用的sort又是同样的field的话,这样的话可以只遍历前面N个doc即可。
请求样例
GET /events/_search
{
"size": 10,
"sort": [
{ "timestamp": "desc" }
]
}
这个查询因为没有query,所以lucene会直接去取每个segment的前N条即可,剩下的会被收集用来计算total_number,假如不需要total_number的话可以在查询当中设置 "track_total_hits": false
这样的话es在找到N个doc后就立即返回相对来说快了很多。
如果query里面有agg操作的话,会忽略"track_total_hits": false
的设置,还是会获取所有命中的doc。
GET /events/_search
{
"size": 10,
"sort": [
{ "timestamp": "desc" }
],
"track_total_hits": false
}
{
"_shards": ...
"hits" : {
"max_score" : null,
"hits" : []
},
"took": 20,
"timed_out": false
}
这里顺便提一下lucene的查询机制,lucene是以segment作为查询单位的,每个segment也被称为sub-index。
索引排序对于组织Lucene doc ID(不要和es中的_id弄混了)很有用,其方式是使AND 查询(a AND b AND…)更有效。为了高效,AND 查询依赖于以下事实:如果任何子查询不匹配,则整个查询都不匹配。通过使用索引排序,我们可以将不匹配的文档放到一起,这将有助于有效地跳过与连接符不匹配的大范围文档ID。
此技巧仅适用于低基数字段(也就是说这个字段的值只有有限个数,但是doc的数量可以很大)。一条经验法则是,您应首先对基数都很低且经常用于过滤的字段进行排序。排序顺序(升序或降序)无所谓,因为我们只关心将与相同子句匹配的值彼此靠近。
例如,如果您要索引要出售的汽车,则按燃料类型,车身类型,品牌,注册年份以及最终里程来分类可能会很有用。
For instance if you were indexing cars for sale, it might be interesting to sort by fuel type, body type, make, year of registration and finally mileage.