目录

  • 一:分片延迟分配
  • NOTE
  • 二:批量请求
  • 三:存储
  • 四:段合并
  • 五:索引刷新频率
  • 六:关闭副本
  • 七:友好的ID
  • 八:日志记录
  • 九:节点下线
  • 十:使用 multiple workers/threads发送数据到ES
  • 十一:减少索引并发访问磁盘
  • 十二:Rollover 自动拆分索引


ES数据写入过程:

数据写入请求——> 
协调节点接收后数据路由处理——> 
存入对应数据节点的 index buffer 并记录 translog 日志——> 
经过 refresh 刷新为 segment 存入文件缓存并变为可搜索——>
数据永久刷新到磁盘并清空 translog 日志
在了解了数据写入过程后,我们可以针对写入过程进行优化。

一:分片延迟分配

说明:
ES会自动在可用节点间进行分片均衡,包括新节点的加入和现有节点离线都会触发这个动作。

例:

  1. 在集群中,有一个node因为故障导致意外关机重启
  2. Master立即注意到这个节点重启,他会把集群内其他节点拥有掉线节点主节点对应的副本升级为主节点
  3. 在副本被提升为主节点后,master节点开支执行重建确实副本的操作,集群内的节点间互相拷贝数据,导致网卡,IO性能增加
  4. 由于集群现在处于非平衡状态,这个过程可能会触发小规模的分片移动。其他不相关的分片将会在节点间迁移来达到一个最佳的平衡状态
  5. 刚刚掉线的节点重启好了,重新加入集群,被告知,当前的数据已经失效了,节点会把当前数据删除,然后集群又开始重新分配分配,引起集群内资源的消耗
  6. 在日常工作中,主机和服务的重启都无法避免,如果重启时间比较短的情况下,如果发生上述的过程,会消耗过多的资源,这是我们不希望看到的。

操作:

  1. 默认情况下,集群会等待一分钟来查看节点是否会重新加入集群,如果集群在这个时间内重新加入集群,重新加入的节点会保持现有的分配数据,不会触发新的分片分配。
  2. 通过delayed_timeout,默认等待时间可以全局设置,也可以在索引级别进行设置:
PUT /_all/_settings 
{
    "settings": {
        "index.unassigned.node_left.delayed_timeout": "5m" 
    }
}
  • _all: 所有索引,也可以修改为单个索引名。
  • 5m: 默认时间修改为5min,也可以设置为0m,立即分配

NOTE

延迟分配不会阻止副本被提拔为主分片。集群还是会进行必要的提拔来让集群回到 yellow 状态。缺失副本的重建是唯一被延迟的过程。

自动取消分片迁移

  1. 如果节点在超时之后回来,集群还没有完成分片的迁移操作,这个时候,Elasticsearch会检查上线的节点的数据是否和当前活跃主分片的数据是否一直,如果两者匹配,说明没有新的文档存储进来,这个时候,集群会取消分片的操作,并启用改节点上的数据,因为磁盘比网络更快,这样更合乎常理。
  2. 但是如果已经产生分歧,在节点离线这段时间内,离线节点的主索引的数据不一致,这个时候还是会按照正常流程来进行操作。

二:批量请求

说明:
在进行写操作时,要确保批量请求是轮询的发往你的所有数据节点,不要把所有的请求都发给单个节点,因为这个节点会需要在处理的时候把所有的批量请求都存在内存里。

三:存储

  1. 使用读写性能比较好的磁盘,比如SSD
  2. 使用RAID 0,带区卷会提高磁盘I/O性能,不要使用镜像卷和校验卷,应为副本已经提供了冗余的功能
  3. 使用多块磁盘,并允许Elasticsearch通过path.data目录配置把数据分配到其中
  4. 不要使用远程挂载的存储,比如NFS或者SMB/CIFS,引入网络延迟对性能来说完全是背道而驰。

四:段合并

段合并的计算量庞大,而且还要吃掉大量磁盘I/O。合并在后台定期操作,因为他们可能要很长时间才能完成,尤其是比较大的段。这个通常来说都没问题,因为大规模合并段的概率是很小的。

  1. 如果发生合并影响写入速度,Elasticsearch会自动限制索引请求到单个线程,这个可以防止段爆炸的问题,即数以百计的段在合并之前就生成出来。如果Elasticsearch发现合并拖累索引了,它会记录一个申明有now throttling indexing的INFO级别信息。
  2. elasticsearch的默认设置比较保守,默认值为20MB/s,对机械磁盘这个值还不错,如果使用的SSD磁盘,这个值应该在100-200MB/s。
PUT /_cluster/settings
{
    "persistent" : {
        "indices.store.throttle.max_bytes_per_sec" : "100mb"
    }
}
  1. 如果我们在做数据的批量导入操作,我们可以彻底关闭合并限流。这可以让你的索引跑到磁盘性能的极致。
PUT /_cluster/settings
{
    "transient" : {
        "indices.store.throttle.type" : "none" 
    }
}
  1. 可以增加index.translog.flush_threshold_size的值,默认为512,这可以清空触发事物日志里累计出更大的段,从而构建出更大的段,清空的频率变低,大段合并的频率也变低。这会导致更少的磁盘I/O和索引速率,但是你需要更大的heap内存用来存储更大的缓冲空间。
  2. 对于refresh而言,每一次的操作,都会产生一个段,而后台又会自动将小段合成一个比较大的段,但是对于ES来说,段还是比较小的,默认来说,每台主机,上的每个shard,会被合并成17个段左右,每个段都会消耗文件句柄,内存和cpu运行周期,更重要的是,每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢。
  3. 段的合并默认是系统自己进行的,但是我们也可以强制合成更多的段;强制合并应该只针对只读索引,在写索引上执行下列操作,会产生超过5GB的索引,并且合并策略不会再考虑对它进行合并,会导致非常大的段留在碎片中。官网资料
# 强制每个shard合并成一个段
POST web-2018.06.08/_forcemerge?max_num_segments=1

五:索引刷新频率

index level:调整刷新频率

  1. 如果我们不需要接近实时的获取数据,我们可以把索引的频率调长一点,修改“index.refresh_interval”参数为30s。
  2. 如果在做大批量的导入,我们可以关闭刷新,将参数的值修改为“-1”,当然在最后别忘了开启。

node level:调整索引缓冲区

  1. 索引缓冲区用于存储新索引的文档,存于内存中,索引缓冲区的默认大小为10%,当他的空间使用满了以后,会将索引从内存中写入到磁盘缓冲区,生产segment,并变为可搜索状态。
  2. 如果我们需要修改这个配置,必须在cluster中的每个data节点进行修改,这是一个静态配置:
indices.memory.index_buffer_size    接受百分比或字节大小值,默认为10%,意味着分配给node的总内存的10%用于索引缓冲区
indices.memory.min_index_buffer_size    如果将index_buffer_size设置为备份比,则可以用此设置指定绝对最小值,默认为48mb
indices.memory.max_index_buffer_size    如果将index_buffer_size设置为百分比,则可以用此设置指定绝对最小值,默认无限制

六:关闭副本

  1. 文档在复制的时候,整个文档内容都被发往副本节点,然后逐字的把索引过程复制一遍,这也意味着副本也会执行分析,索引以及可能的合并过程。
  2. 如果索引是零副本,在写入后再开启副本,恢复过程本质上只是复制的操作。

七:友好的ID

  1. 如果你没有给文档指定id,那么Elasticsearch会自动生成ID
  2. 如果要使用自己的ID,想UUID-4这样的ID,本质上是随机的,压缩比很低,会明显拖慢lucene,这个使用我们就需要用lucene友好的ID。

八:日志记录

日志等级

  1. Elasticsearch默认的日志等级为INFO,他提供了适度的信息,但是又不至于让日志过于庞大。
  2. 但是当我们排错时,INFO的日志信息局限性又比较大,这个时候就需要调整日志级别到DEBUG,我们可以通过logging.yml文件设置,但是这样需要重启。
  3. 我们也可以通过API动态调整。
PUT /_cluster/settings
{
    "transient" : {
        "logger.discovery" : "DEBUG"
    }
}

慢日志

  1. 慢日志默认是不开启的,如果需要开启,要定义动作(query,fetch和index),你期望的记录等级,还有时间阈值。
PUT /my_index/_settings
{
    "index.search.slowlog.threshold.query.warn" : "10s", 
    "index.search.slowlog.threshold.fetch.debug": "500ms", 
    "index.indexing.slowlog.threshold.index.info": "5s" 
}
  • 查询慢于10s的输出一个WARN日志
  • 获取慢于500ms的输出一个DEBUG日志
  • 索引慢于5s的输出一个INFO日志
  1. 这个日志也可以在logging.yml文件中设置
  2. 一但阈值设置过了,就可以像其他日志一样切换日志等级:
PUT /_cluster/settings
{
    "transient" : {
        "logger.index.search.slowlog" : "DEBUG", 
        "logger.index.indexing.slowlog" : "WARN" 
    }
}
  • 设置搜索慢日志为DEBUG级别
  • 设置索引慢日志为WARN级别

九:节点下线

十:使用 multiple workers/threads发送数据到ES

多进程或者线程

如果看到TOO_MANY_REQUESTS (429)和EsRejectedExecutionException则说明ES跟不上索引的速度,当集群的I/O或者CPU饱和就得到了工作者的数量。

十一:减少索引并发访问磁盘

如果是机械硬盘,你需要增加下面的配置到elasticsearch.yml中

index.merge.scheduler.max_thread_count: 1

机械硬盘的并发IO性能较差,我们需要减少每个索引并发访问磁盘的线程数,这个设置会有max_thread_count+2个线程并发访问磁盘
如果是SSD可以忽略这个参数,默认线程数是Math.min(3, Runtime.getRuntime().availableProcessors() / 2),对于SSD来说没有问题。

十二:Rollover 自动拆分索引

当满足指定的条件后,索引就会被自动拆分

指定方法:

PUT /logs-000001  
{ 
  "aliases": { 
    "logs_write": {} 
  } 
} 
 
# Add > 1000 documents to logs-000001 
 
POST /logs_write/_rollover  
{ 
  "conditions": { 
    "max_age":   "7d",   # 最大的时间限制
    "max_docs":  1000    # 最大的条数
  } 
}

注意事项:

  • 索引命名规则必须如同:logs-000001。
  • 索引必须指定 aliases。
  • Rollover Index API 调用时才去检查索引是否超出指定规则,不会自动触发,需要手动调用,可以通过 Curator 实现自动化。
  • 如果符合条件会创建新的索引,老索引的数据不会发生变化,如果你已经插入 2000 条,拆分后还是 2000 条。
  • 插入数据时一定要用别名,否则你可能一直在往一个索引里追加数据。

示例:

假如生成的索引名为 logs-2017.04.13-1,如果你在当天执行 Rollover 会生成 logs-2017.04.13-000001,次日的话是 logs-2017.04.14-000001。

每天学习一点点,重在积累!