ElasticSearch亿级数据毫秒查询实现

  1. 性能优化:Filesystem Cache

ES里写的数据,实际是存到磁盘,查询搜索时,将磁盘文件里的数据自动缓存到 Filesystem Cache 里面去。

如果给 Filesystem Cache 更多的内存,尽量让内存可容纳所有的 IDX Segment File 索引数据文件,那搜索时基本都走内存,性能非常高。要让 ES 性能好,最佳的情况下,就是你的机器的内存,至少可以容纳你的总数据量的一半。

  1. 架构优化

架构:ES + HBase,在 ES 中就存少量的数据少数关键字段,其他字段数据存在 MySQL/HBase 里。

HBase是列式数据库,其特点是适用于海量数据的在线存储,就是对 HBase 可以写入海量数据,但不做复杂搜索,做key-value查询或范围搜索。写入 ES 的数据最好小于等于,或者是略微大于 ES 的 Filesystem Cache 的内存容量。

  1. 缓存预热子系统:

比较热的、常访问的数据,做一个专门的缓存预热子系统。对热数据每隔一段时间,提前访问一下,让数据进入 Filesystem Cache 里面去。

  1. 冷热分离

ES 可以将大量的访问很少、频率很低的数据,单独写一个索引,然后将访问很频繁的热数据单独写一个索引。
最好将冷数据写入一个索引中,然后热数据写入另外一个索引中,这样可以确保热数据在被预热之后,尽量都让他们留在 Filesystem OS Cache 里,别让冷数据给冲刷掉。

  1. ES中的关联查询

ES 里复杂的关联查询尽量别用,一旦用了性能一般都不太好。最好是先在 Java 系统里就完成关联,将关联好的数据直接写入 ES 中。搜索的时候,就不需要利用 ES 的搜索语法来完成 Join 之类的关联搜索了。

  1. Document 模型设计

Document 模型设计是非常重要的,很多操作,不要在搜索的时候才想去执行各种复杂的乱七八糟的操作。
ES 能支持的操作就那么多,不要考虑用 ES 做一些它不好操作的事情。如果真的有那种操作,尽量在 Document 模型设计的时候,写入的时候就完成。另外对于一些太复杂的操作,比如 join/nested/parent-child 搜索都要尽量避免,性能都很差的。

  1. 分页性能优化

ES 的分页是较坑的,因为要先聚合、排序、筛选等,最后再次分页,拿到里面某页的数据。
翻的越深,每个 Shard 返回的数据就越多,协调节点处理的时间越长,越翻到后面,就越慢。

解决思路:

  • 不允许深度分页(默认深度分页性能很差)。默认翻的越深,性能就越差。- 类似于 App 里的推荐商品不断下拉出来一页一页的;类似于微博中,下拉刷微博,刷出来一页一页的,可以用 Scroll API。
  • Scroll是如何做的呢?它会一次性给你生成所有数据的一个快照,然后每次滑动向后翻页就是通过游标 scroll_id 移动,获取下一页、下一页这样子,性能会比上面说的那种分页性能要高很多很多,基本上都是毫秒级的。

缺点:不能随意跳页。只能往下拉,一页一页的翻。

使用时需要注意,初始化必须指定 Scroll 参数,告诉 ES 要保存此次搜索的上下文多长时间。你需要确保用户不会持续不断翻页翻几个小时,否则可能因为超时而失败。
除了用 Scroll API,也可以用 search_after 来做。search_after 的思想是使用前一页的结果来帮助检索下一页的数据。
显然,这种方式也不允许你随意翻页,你只能一页页往后翻。初始化时,需要使用一个唯一值的字段作为 Sort 字段。