本文主要讨论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

锁定内存需要系统配置:

  1. 修改/etc/security/limits.conf
  2. 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)

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐