ES写入性能优化

背景: 有1亿多的用户画像中数仓需要导入ES。大多数字段都是sql统计数据,无法区分哪些发生了变化,所以不能增量更新。只能每天全量刷数据。在刷数据的过程中出现了更新缓慢、内存问题。于是做了一些写入优化。


解决方案

1. 读数据

首先要从数仓读取出数据到内存。然后再组装对象去ES刷数据字段比较多而且都需要查询。尝试了一下,即使limit 10,也需要耗时2分钟。所以第一步导数据不能直接查询。采用的是数仓到分布式文件系统分片存储。这一步已经有现成工具。1亿数据导入到分片耗时3分钟左右

2.组装数据

将分片的数据读到java内存中。完成从文件到List的转化。之前写业务代码数据量一般不是很大,采用的是一次性把数据读取到内存中。再做业务处理。但是这次在数据塞到一半的数据,先是系统响应变慢了,后来测试环境的系统挂了。通过过命令排查,发现List对象占用了很多空间。于是复查代码。发现是for循环一直往list填对象导致的内存泄露。于是限制了单个文件大小为20M,一个文件一个文件地处理。再把List转化为es批量bulk的请求参数,以用户id作为es的_id。听说这样会导致索引速率变慢。但可以减少一些每天冗余的数据,而且以后如何增量了也比较好拓展。ES建议每次bulk5~15M数据,这里我每次批量提交5000条,bulk的时候发生的413 requets too large错误,google了一下,说是索引的时候段合并内存不够。于是调整indices.breaker.fielddata.limit为60%,增大堆内存,结果没什么用;也有说要调整 client_max_body_size 的,但是我们的es是云服务,没法改配置参数最终加大es的内存为16G,不再报这个错误。

问题:1.大list在内存里使得内存溢出。ES建议每次bulk5~15M数据,这里我每次批量提交5000条,bulk的时候发生的413 requets too large错误,google了一下,说是索引的时候段合并内存不够。于是调整indices.breaker.fielddata.limit为60%,增大堆内存,结果没什么用;也有说要调整 client_max_body_size 的,但是我们的es是云服务,没法改配置参数最终加大es的内存为16G,不再报这个错误

3.提高es索引效率

刚开始刷数据预计需要20个小时。今天的数据如果明天才更新完,意义不大。于是想办法提高索引效率。网上都说"refresh_interval": "-1";调整number_of_replicas=0。我调整了结果没什么变化。于是采用多线程刷数据

问题:1.一开始使用size为20的无界队列,导致耗尽资源,任务线程占用的内存占用了80+%的内存,其他任务可能被拖垮。后来线程的核心线程数和最大线程数统一设置为10。并采用future模式,一个任务完成后再去添加其他任务。解决了线程耗尽资源和内存的问题。

用htop查看刷数据机器的性能

可以看到开启的10个线程占用42%内存。主线程cpu偶尔接近100%,这不是io密集型吗?怎么会耗cpu。cpu变高可能是复杂的技术或者死循环。这里循环每次读取量有50000条,并且组装对象的逻辑。而且有10个线程,猜想可能是这个原因。

ES的索引速率

成果

最后原来需要20小时才能完成的刷数据任务,只耗时约100分钟。当然中间遇到的坑不止这些

猜你喜欢

转载自juejin.im/post/5c73d8ea518825622d74c138