Elasticsearch bulk request
2018-12-22
最近在往ES里写数据的时候,发现一条一条写非常慢。基本一个比较小规模(3master、3data、1client)的ES集群on k8s,用RestHighLevelClient写入数据,加上一些业务逻辑,每秒只能写到30条左右。
线上环境有一张3500w的表,这样写实在不能忍,于是换成ES的bulk request,发现速度快了很多,每秒5000条。
我们是这样设置的,由于ES对bulk request的一次请求大小有限制,要求在20M以内,所以我们采用1000条作为一个bulk,目前看效果很不错,应该还可以继续优化,要求是200w/s,还有一定差距。。。
2018-01-17
ES官方推荐的一次bulk size介于5-15MB之间,请求个数是1000-5000个
但是实际环境里,一条数据的大小是不确定的,而且不同规模的集群,bulk的大小也不一样,具体设置还是要看情况
不过有这样一个办法,可以大致估计一个请求的大小
将这个请求序列化出来,然后看大小,达到最佳size后就发送
这样有几个问题:
- 每个数据都执行这个操作吗?
- 序列化本身就是一件很耗cpu的事情,这样不会很慢吗?
- 序列化以后的数据要占据的空间是MB级别,回收这个空间的消耗值得吗?
这个问题比较有意思,spark的代码里有一个类 SizeEstimator,他的用途其实和我们的需要是一样的,用来估计一个对象在内存中的大小,但是有个问题,他估计的是这个对象。如果这个对象有一个字段是指针,那这个指针指向的另一个对象也会被序列化,有空可以研究一下,如何改进这个类,达到我们的目的。
在索引数据过程中,经常发现,即使使用了最佳的bulk size,也不能达到最快的速度,因为我们的数据来源虽然是impala数据,但是实现上并没有使用impala的JDBC接口。而是直接用HDFS的FileSystem打开文件流,理论上最快的速度应该达到文件流的速度,然而实际上远远没有。目前的测试情况,最快的速度也只有1w条数据/s,与目标相差甚远😭。
所以目前的设计方案除了继续优化代码,减少不必要的性能损失以外,还有一个想法就是集群化,并且多线程化,大概设想如下:
- 首先每个任务有自己的线程池,在集群启动时,预估整个集群的资源,并按需分配,这部分可以参考spark的逻辑,然后以文件数为例,对impala数据来说,最终数据都存在HDFS上,所以可以先查看HDFS的数据集文件大小,大概每个block的量(256MB),作为一个线程去做索引,如果这个表含有的数据大小约等于3个block,实际上是5份文件,那就起3个线程,平均每个线程分得的数据量最好一致,以此类推。
- 集群化则是说,可以有一个service提供服务,然后索引实例有n个,没有master,最好彼此之间互不影响,并且能够完成任务的自动调度,能够统一提供索引服务,最大化利用资源,让索引速度快起来。