乐趣区

ES写入性能优化

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

解决方案:
1. 读数据

首先要从数仓读取出数据到内存。然后再组装对象去 ES 刷数据字段比较多而且都需要查询。尝试了一下,即使 limit 10,也需要耗时 2 分钟。所以第一步导数据不能直接查询。采用的是数仓到分布式文件系统分片存储。这一步已经有现成工具。1 亿数据导入到分片耗时 3 分钟左右
2. 组装数据
将分片的数据读到 java 内存中。再构造请求参数刷 ES
` 问题:1. 刷数据 ES 报 413 错误。ES 建议每次 bulk5~15M 数据,这里我每次批量提交 5000 条,bulk 的时候发生的 413 requets too large 错误,google 了一下,说是索引的时候段合并内存不够。于是调整 indices.breaker.fielddata.limit 为 60%,增大堆内存,结果没什么用;也有说要调整 client_max_body_size 的,但是我们的 es 是云服务,没法改配置参数最终加大 es 的内存为 16G, 不再报这个错误。
2. 之前写业务代码数据量一般不是很大,采用的是一次性把数据读取到内存中。再做业务处理。但是这次在数据塞到一半的数据,先是系统响应变慢了,后来测试环境的系统挂了。通过过命令排查,发现 List 对象占用了很多空间。于是复查代码。发现是 for 循环一直往 list 填对象导致的内存泄露。于是限制了单个文件大小为 20M, 一个文件一个文件地处理。`
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 分钟。当然中间遇到的坑不止这些

退出移动版