ElasticSearch 海量数据查询性能优化
ES 接收到查询请求后,会转发给所有相关的 Shard 分片,每个 Shard 在自己这块儿进行搜索,各自的结果汇总后再返回给客户端。
这个过程有2个核心性能关键点:
- Shard 执行查询计算的耗时
- Shard 读取各个 Segment 文件的 I/O 耗时
所以,我们就要从这两个角度进行优化。
- 尽量让 ES 的查询动作简单,避免进行复杂的查询计算。
- 尽量提升 I/O 效率,也就是用好文件系统缓存(从 ES 写入流程可以知道文件缓存有很大的作用)
尽量让查询计算简单
这就涉及到 ES 中的数据建模方式。
例如关系数据库有两张表:
- 订单表(编号、总金额、用户、日期)
- 订单详情表(订单编号、商品信息)
查询订单时,需要对两张表进行关联查询。
这在关系数据库中很正常,因为关系数据库是根据实体及其关系来建模的。
但在 ES 中最好就不要这么定义数据结构了,ES 是搜索引擎,要以搜索的角度来规划数据,怎么便于搜索怎么来。
例如直接把这两张表的数据放在一个Document中,一个简单查询就完事儿了,不用让 ES 进行Document的关联查询。
最大化使用操作系统的文件缓存
操作系统喜欢用文件缓存,只要内存充足,读取 Segment 文件的时候,就会将其内容一直放在缓存中,直到内存不够用了。
比如读 Segment 01 这个文件的时候,先加载到文件缓存。
如果缓存空间足够,就直接加载进来。
否则,就会移除一部分缓存的文件,腾出空间后再加载进来。
例如,你的 ES 数据量是 1000G,集群有3个节点,每个节点上面就是 300 多个G。
假设,每个节点中操作系统可以使用内存为 16G,那么,搜索的时候,直接读文件缓存的最大概率也只有 5% 左右,绝大部分的数据都要读物理文件,一起争用那一点文件缓存空间,性能自然很差。
优化方向有 2 个:
1)增加内存
2)减少 ES 中的数据量
增加内存
增加内存很简单,只要有钱就行。
那么需要增加到多少呢?
建议:操作系统可用内存 > 本节点数据量的 50%
这样数据命中缓存的概率就比较高了。如果不差钱,内存越大越好。
减少 ES 中的数据量
再强调一次 ES 是搜索引擎,是用来干搜索的。
很多人喜欢把数据全都放 ES 里,感觉它是分布式的,多放几台服务器,硬盘大点,很能装。
比如一个表有上百个字段,真正搜索的时候,都会用到吗?不一定吧。
那么,搜索用不到的数据尽量别放在 ES 里。
ES 服务于搜索,不是服务于存储。
所以,ES 中应该放与搜索相关的数据,不应该放全量数据。
全量数据的存储应该使用专业的存储系统,例如 HBASE。
在大数据环境中,有几个T的数据很正常,如果都放在 ES 里面,并且想要保障查询性能,内存要多大呢?
如果经济实力不允许,内存不是很充足,那么查询性能就很悲催了,每次查询花费几秒钟很正常。
如果 ES 中只放搜索相关的少量数据,把全量数据放在 HBASE,性能就会快很多。
先从 ES 中查询出结果数据集,其中只包含核心字段,然后根据结果集中各条数据的 Key 到 HBASE 中进行精准查询,再把结果进行整合。
虽然是两次查询,但因为每部分都很快,所以整体下来也很快。
思路扩展
冷热分离
数据通常有冷热之分,有一部分数据的使用频率明显高于其他数据。
如果文件系统的缓存不是很充足,那么数据就需要轮流呆在缓存中。
热数据自然呆的时间长,但是当加载冷数据时,就会把热数据挤出缓存了。那么下次就需要从磁盘重新加载。
所以,最好让热数据走一部分 Shard,冷数据走其他 Shard,防止冲刷缓存。
数据预加载
还是基于热数据的角度,可以事先调用一下,使其提前进入文件缓存。
这样,客户第一次查询的时候也会很快。
小结
想要 ES 查询速度快,需要使查询动作尽量简单,这需要在数据建模上多花些心思。
最重要的是要利用好文件缓存。
增加内存是简单、粗暴、有效的方法。
还有就是尽量缩减 ES 中的数据量,不要存放与搜索需求无关的数据。可以配合 HBASE 这类的专业海量数据存储系统一起使用。
另外,冷热数据分离、数据预加载,也是比较有效的小技巧。