本文主要讨论es加载速度的优化,有些优化会导致部分功能及数据安全性丧失,需要理性使用。
网络中大部分的性能优化方案基本源于官网,如下位置:
以下优化包含但不限于官方文档:
整体写入操作影响因子:
1. indice相关设置
{
"settings": {
"number_of_shards": n,//分片尽量设置多一些,能提高加载速度
"number_of_replicas": 0,//副本设置为0,丧失数据安全性
"refresh_interval": "-1",//刷新时间设置为-1,丧失数据实时性
"merge": {
"scheduler": {
"max_thread_count": "1" //合并时最大线程数
},
"policy": {
"max_merged_segment": "5gb",//段超过多大就不参与合并
"segments_per_tier": "24"
}
},
"translog.durability": "async",//异步translog,后续具体分析
"translog.flush_threshold_size": "2gb",//这个参数表示在当未提交的translog日志达到该阈值的时候进行一次刷盘操作。
"translog.sync_interval": "100s"
},
"mappings": {
"_doc": {
"properties": {
"name": {
"type": "keyword",
"doc_values":false, //不存储doc,丧失聚合排序等功能
"index":false //不索引,丧失查询功能
}
}
}
}
}
2. cluster相关设置
indices.memory.index_buffer_size: 30% //加大索引数据的缓存
discovery.zen.ping_timeout: 60s //超时时间,默认3s
discovery.zen.fd.ping_interval: 30s //节点多久ping一次master,默认1s
discovery.zen.fd.ping_timeout: 60s //等待响应时间,默认30s
discovery.zen.fd.ping_retries: 6 //失败或超时后重试的次数,默认3
thread_pool.write.size: cpus+1 //设置写线程数,默认cpus
thread_pool.write.queue_size: 2000 //加大数据写队列,默认200
具体分析以上配置:
3. 关闭某些存储结构
不需要聚合:doc_values:false
不需要返回数据:_source:false
不需要检索:index:false
不算分:norms:false
能用keyword就不用text
Index_options设置倒排索引具体内容
……
4. bulk合理使用
Resthighclient和Bulkprocess.add()均为线程安全,可多线程调用一个实例添加数据。
控制client提交bulk在多节点间轮训(client.transport.sniff :true/restclient添加多个节点)
设置bulk thread_poll线程数为cpu+1 ,增大队列2000(不要太大,会GC)
setBulkSize(5mb):大小建议是5-15MB,默认不能超过100M
bulkActions(1000):1000-5000个文档,如果你的文档很大,可以适当减少队列
bulk的concurrentRequests:默认是1
多线程并发写入,可以减少每次底层磁盘fsync的次数和开销。
一旦发现es返回了TOO_MANY_REQUESTS的错误,JavaClient也就是EsRejectedExecutionException。此时那么就说明es是说已经到了一个并发写入的最大瓶颈了。
如果加载过程中检测到HTTP429返回表示集群压力过大
5. 增加/禁用refresh间隔
refresh默认1s:用index.refresh_interval参数可以设置,这样会其强迫es每秒中都将内存中的数据写入磁盘中,创建一个新的segment file。
延长或者禁止refresh
PUT /test/_settings
{ "refresh_interval": "30s" }
PUT /test/_settings
{ "refresh_interval": -1 }
手动刷新数据查询:
POST /_refresh
POST /test/_refresh
6. 关闭swap 内存交换
如果要将es jvm内存交换到磁盘,再交换回内存,大量磁盘IO,性能很差。
将/etc/fstab 文件中包含swap的行注释掉
sed -i '/swap/s/^/#/' /etc/fstab
系统关闭:
swapoff -a/vm.swappiness = 1
es锁定内存:
2.4以前:
bootstrap.mlockall: true
新版本:
bootstrap.memory_lock:true
锁定内存需要系统配置:
- 修改/etc/security/limits.conf
- ulimit -l unlimited
7. filesystem cache相关
filesystem cache被用来执行更多的IO操作,如果我们能给filesystemcache更多的内存资源,那么es的写入性能会好很多。即保证运行es实例的服务器有多余闲置内存供给(除去配置的堆内存)
8. 使用自动生成的id
如果我们要手动给es document设置一个id,那么es需要每次都去确认一下那个id是否存在,这个过程是比较耗费时间的。如果我们使用自动生成的id,那么es就可以跳过这个步骤,写入性能会更好。对于你的业务中的表id,可以作为es document的一个field。
9. buffer相关
如果我们要进行非常重的高并发写入操作,那么最好将index buffer调大一些,indices.memory.index_buffer_size,这个可以调节大一些,设置的这个index buffer大小,是所有的shard公用的,但是如果除以shard数量以后,算出来平均每个shard可以使用的内存大小,一般建议,但是对于每个shard来说,最多给512mb,因为再大性能就没什么提升了。es会将这个设置作为每个shard共享的index buffer,那些特别活跃的shard会更多的使用这个buffer。默认这个参数的值是10%,也就是jvm heap的10%,如果我们给jvmheap分配10gb内存,那么这个index buffer就有1gb,对于两个shard共享来说,是足够的了。
索引缓冲区用于存储新索引的文档。当它填满时,缓冲区中的文档将写入磁盘上的段。它在节点上的所有分片之间划分。
必须在群集中的每个数据节点上进行配置:
1. indices.memory.index_buffer_size:30%
设置百分比 or 字节大小值。默认为10%,意味着10%分配给节点的总堆将用作所有分片共享的索引缓冲区大小。
2. indices.memory.min_index_buffer_size
如果index_buffer_size设置为百分比,则可以使用此设置指定最小值。默认为48mb。
3. indices.memory.max_index_buffer_size
如果index_buffer_size设置为百分比,则可以使用此设置指定最大值。默认为无限制。
10. translog相关
1. index.translog.sync_interval
translog写入磁盘并提交频率。默认为5s。小于的值100ms是不允许的。
2. index.translog.durability
是否在每个索引,删除,更新或批量请求之后提交translog。此设置接受以下参数:
request(默认):每个请求后提交。如果发生硬件故障,所有已确认的写入都已提交到磁盘。
Async:每隔sync_interval时间提交。如果发生故障,将丢弃自上次自动提交以来的所有已确认写入。
3. index.translog.flush_threshold_size
一旦事务日志达到这个值,就会发生一次flush;默认值为512mb。调大就可以减少flush的次数以及大段的合并次数。可以适当调大,但不能超过indexBufferSize*1.5倍/(可能并发写的大索引数量),否则会触发限流,并导致JVM内存不释放。
4. generation_threshold_size
默认64M,系统支持,但官方文档没有的参数,超过该阈值会产生新的translog文件,要小于index.translog.flush_threshold_size,否则会影响flush,进而触发限流机制。
示例:
"translog.durability": "async",
"translog.flush_threshold_size": "2gb",
"translog.sync_interval": "100s",
"translog.generation_threshold_size":"64mb"
11. 段合并策略
老版本策略:
PUT /_cluster/settings
{
"persistent" : {
"indices.store.throttle.max_bytes_per_sec" : "100mb"
}
}
设置限流类型为 none 彻底关闭合并限流。等你完成了导入,记得改回 merge 重新打开限流
PUT /_cluster/settings
{
"transient" : {
"indices.store.throttle.type" : "none"
}
}
"merge.policy.max_merged_segment": "1000mb",
当前策略:
"merge" : {
"scheduler" : {
"max_thread_count" : "1" //merge时最大的线程数
},
"policy" : {
"max_merged_segment":"5gb",//超过此大小的segment不再参与合并
"floor_segment" : "2g", //默认 2MB,小于这个大小的 segment,优先被归并
"segments_per_tier" : "24", //每个tier允许的segement 数,越小需要合并的操作越多,要大于at_once
"max_merge_at_once" : "5" //默认一次最多归并 10 个 segment
}
}
merge 策略有三种:
tiered
log_byete_size
log_doc
默认情况下:
1. index.merge.polcy.type: tiered
索引创建时合并策略就已确定,不能更改,但是可以动态更新策略参数,一般情况下,不需要调整.如果堆栈经常有很多 merge, 可以尝试调整以下配置:
2. index.merge.policy.floor_segment
该属性用于阻止segment 的频繁flush, 小于此值将考虑优先合并,默认为2M,可考虑适当降低此值
3. index.merge.policy.segments_per_tier
该属性指定了每层分段的数量,取值约小最终segment 越少,因此需要 merge 的操作更多,可以考虑适当增加此值.默认为10,他应该大于等于 index.merge.policy.max_merge_at_once
4. index.merge.policy.max_merged_segment
指定了单个 segment 的最大容量,默认为5GB,可以考虑适当降低此值
12. spark的使用
使用spark写es,利用分布式的资源
13. didi开源的fastindex分析
**核心思想:**启动多个java进程(可以利用mr/spark)构造多个es实例写数据,然后将多个es实例的data目录下的索引文件(lucene索引)合并到线上集群的真实索引中(使用lucene自带的indexWriter.addIndexes(*) api)