如何针对高写入低查询es集群优化

针对高并发写入的日志集群、告警集群做从日志写入、处理、集群优化提高写入吞吐量

概述:ELK现在已经成为大多数公司作为日志存储、告警的通用解决方案,目前我们现网除了业务日志走大数据之外,其他组件内部支持皆为elk存储告警.
集群规模:
es集群为 28节点 每个节点5.5T盘机械硬盘 存储数据约7天/55T 写入约12w/s

高写入低查询集群:

向索引中插入数据时,文档首先被保存在内存缓存(in-memory buffer)中,同时将操作写入到translog中,此时这条刚插入的文档还不能被搜索到。默认1秒钟refresh一次,refresh操作会将文件写到操作系统的文件系统缓存中,并形成一个segment,refresh后文档可以被检索到。
当flush的时候,所有segment被同步到磁盘,同时清空translog,然后生成一个新的translog,Lucene把每次生成的倒排索引叫做一个segment,也就是说一个segment就是一个倒排索引

##日志格式优化:
日志格式/字段:
日志格式统一采JSON,便于 ELK 解析处理。日志中的各个字段的值,都应该尽量使用 英文 ,不使用中文。
日志具体字段:分为 基础数据 + 扩展数据。基础数据,是底层日志框架自带的,所有日志都需包含。扩展数据,不同类型的日志,包含不同的字段
日志基础数据:

  • hostname: string 主机名
  • hostip:string 主机ip
  • timestamp:date 日志产生时间(UTC 格式的日期)
  • module:string 服务名称
  • ret:int 状态码
  • cluster_tag:string 集群区分(环境变量$RUNENV)
  • calltime:int 耗时
    注意:基础字段可全部包含但必须有可囊括字段(如ret或calltime)
    日志扩展数据:
  • req\resp等请求体,扩展字段: TODO,需位于json第一层字段
  • stat类型日志,扩展字段: { perf: {rss:xxx, oss:xxx} }
  • pipe类型日志,扩展字段: [ log:{rss:xxx, oss:xxx} ]
  • 不带有type字段与logstash冲突导致日志写入失败

写入链路优化:

去除写入多余字段如:req\rep等
压缩无需索引字段,做序列化转义压缩:如
"log": "[{"calltime":5,"methodName":"getStartNode","reqArgs":["5ee0fd60","START"],"result":{"audioUrl":"","name":"开始","nodeId":1,"replyContent":"","toNodeIds":[2],"type":"start"}},{"calltime":6,"methodName":"getNodeById","reqArgs":["5ee0fd60",2],"result":{"audioUrl":"http://aiui.xfyun.cn/index-aiui","name":"摘机问候","nodeId":2,"replyContent":"摘机问候","toNodeIds":[3],"type":"robot"}},{"calltime":4,"methodName":"getNodeById","reqArgs":["5ee0fd60",3],"result":{"audioUrl":"","name":"用户任意说","nodeId":3,"replyContent":"","toNodeIds":[4],"type":"popple"}},{"calltime":4,"methodName":"getNextAnswerNodes","reqArgs":["5ee0fd60",{"audioUrl":"http://aiui.xfyun.cn/index-aiui","name":"摘机问候","nodeId":2,"replyContent":"摘机问候","toNodeIds":[3],"type":"robot"}],"result":[4]}]",

Es cluster集群优化:

机器部署:

日志数据非业务重要可以试试RAID0,写入性能相对RAID1可以提升25-30%

针对translog优化:

translog.flush_threshold_size: 1024mb
translog.durability ==>async异步方式同步

索引刷新优化

部分索引非实时搜索.想优化索引速度而不是近实时搜索,降低索引的刷新频率
"refresh_interval": "30s"

分片副本优化

分片和副本:针对大索引+高写入+存储大小大于150GB
number_of_shards 10
number_of_replicas 1 or 0
根据索引的重要程度+查询速度要求 有无副本

高写入+存储大小大于150GB + 低查询速度 有些无副本 并且分片 略小于节点数

增大静态配置参数

indices.memory.index_buffer_size (默认10%)
示例:索引10shards,1副本,日约3.6亿条,存储量1T左右,设置成30%提高了约10%写入性能

避免出现热点节点

routing.allocation.total_shards_per_node":"2"

调整bulk线程池和队列

部分高频索引自建templates:

写入体较大的索引,如某条日志接近上千行,不建议往es中进行写入,因字段较多,或者日志体中含有无法解析的json体或者dict体。虽然第一层字段类型被定义为text或者数组体。但是内部字段还是会被分离,被mapping.后续大量日志写入的时候就会进行大量的mapping,日志量和日志体都过大或导致merge的时候耗时高,同时进行新旧gc的时候也会耗时很大。也容易导致节点oom

查询优化:

1.通过监控分析确定ES集群节点负载,对高负载节点做定向索引分解和必要时重启散压力操作
2.出现热点节点可试图通过Cerebro 或者自身es-head进行索引分片调整
3.查询时尽量勿选择时间长、通配字符查询、分片数量大

总结过程

规范日志写入格式、日志写入前进行序列化、固化日志内固定字段用于后续告警
升级日志集群版本和新的告警对齐、日志机器统一化、模板化、集群数据节点/协调节点分离
优化集群配置:调整线程池和队列、translog异步优化、索引等级刷新优化、索引分类副本优化、索引
优化写入链路:去除写入多余字段(req/resp等)、压缩字段内部字典/数组日志、做性能日志的耗时丢弃、按照特定规则分割
索引优化部分重要索引优化副本、自建templates、定义好mapping规则
查询优化: 热点数据进行内存缓存快速查询,集群部分节点做冷数据存储,大于3天的数据抛入冷节点,
消费优化: 模板化单key消费模板与cicd打通,由物理机多机器部署升级至容器化部署
缓存优化:因缓存组件无法更换,由单节点redis升级至temproxy多节点分发,针对redis缓存队列配置和写入问题做了优化

总结:

1.如果有能力上SSD+内存大小大于数据60%,可以忽略上述所有
2.如果针对磁盘性能问题可以试试磁盘硬件方面RAID0
3.整体链路优化需要从入口、过滤、写入都需要进行优化
4.最好的优化方法是简于形,规于心