ElasticSearch 优化

ElasticSearch 优化

ES优化的几个方面

  • Filesystem Cache

  • 数据预热

  • 冷热分离

  • ElasticSearch 中的关联查询

  • Document 模型设计

  • 分页性能优化*

Filesystem Cache


你往 ES 里写的数据,实际上都写到磁盘文件里去了,查询的时候,操作系统会将磁盘文件里的数据自动缓存到 Filesystem Cache 里面去。

ElasticSearch 优化_第1张图片

ES 的搜索引擎严重依赖于底层的 Filesystem Cache,你如果给 Filesystem Cache 更多的内存,尽量让内存可以容纳所有的 IDX Segment File 索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高。

归根结底,你要让 ES 性能好:

  • 最佳的情况下,就是你的机器的内存,至少可以容纳你的总数据量的一半。
  • 减少不必要的字段写入ES

数据预热


就是对于那些你觉得比较热的、经常会有人访问的数据,最好做一个专门的缓存预热子系统。

然后对热数据每隔一段时间,就提前访问一下,让数据进入 Filesystem Cache 里面去。这样下次别人访问的时候,性能一定会好很多。

冷热分离


ES 可以做类似于 MySQL 的水平拆分,就是说将大量的访问很少、频率很低的数据,单独写一个索引,然后将访问很频繁的热数据单独写一个索引。

最好是将冷数据写入一个索引中,然后热数据写入另外一个索引中,这样可以确保热数据在被预热之后,尽量都让他们留在 Filesystem OS Cache 里,别让冷数据给冲刷掉。

ES中的关联查询


对于 MySQL,我们经常有一些复杂的关联查询,在 ES 里该怎么玩儿?

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

Document模型操作

Document 模型设计是非常重要的,很多操作,不要在搜索的时候才想去执行各种复杂的乱七八糟的操作。

ES 能支持的操作就那么多,不要考虑用 ES 做一些它不好操作的事情。如果真的有那种操作,尽量在 Document 模型设计的时候,写入的时候就完成。

另外对于一些太复杂的操作,比如 join/nested/parent-child 搜索都要尽量避免,性能都很差的。

分页性能优化*


ES 的分页是较坑的,为啥呢?举个例子吧,假如你每页是 10 条数据,你现在要查询第 100 页,实际上是会把每个 Shard 上存储的前 1000 条数据都查到一个协调节点上。

如果你有 5 个 Shard,那么就有 5000 条数据,接着协调节点对这 5000 条数据进行一些合并、处理,再获取到最终第 100 页的 10 条数据。

由于是分布式的,你要查第 100 页的 10 条数据,不可能说从 5 个 Shard,每个 Shard 就查 2 条数据,最后到协调节点合并成 10 条数据吧?

你必须得从每个 Shard 都查 1000 条数据过来,然后根据你的需求进行排序、筛选等等操作,最后再次分页,拿到里面第 100 页的数据。

你翻页的时候,翻的越深,每个 Shard 返回的数据就越多,而且协调节点处理的时间越长,非常坑爹。所以用 ES 做分页的时候,你会发现越翻到后面,就越是慢。

解决方案

  • 不允许深度查询

    跟产品经理说,你系统不允许翻那么深的页,默认翻的越深,性能就越差。

  • Scroll API

    scroll 查询 可以用来对 Elasticsearch 有效地执行大批量的文档查询,而又不用付出深度分页那种代价。

    ​ 深度分页的代价根源是结果集全局排序,如果请求的页数较少时,假设每页10个docs——即pageSize=10, 此时Elasticsearch不会有什么问题。但若取的页数较大时(深分页),如请求第20页,Elasticsearch不得不取出所有分片上的第1页到第20页的所有docs,假设你有16个分片,则需要在coordinate node 汇总到 shards* (from+size)条记录,即需要 16*(20+10)记录后做一次全局排序,再最终取出 from后的size条结果作为最终的响应。所以,当索引非常非常大(千万或亿),是无法按照 from + size 做深分页的,分页越深则越容易OOM,即便不OOM,也是很消耗CPU和内存资源的。如果去掉全局排序的特性的话查询结果的成本就会很低。 游标查询用字段 _doc 来排序。 这个指令让 Elasticsearch 仅仅从还有结果的分片返回下一批结果。

    ​ 启用游标查询可以通过在查询的时候设置参数 scroll 的值为我们期望的游标查询的过期时间。 游标查询的过期时间会在每次做查询的时候刷新,所以这个时间只需要足够处理当前批的结果就可以了,而不是处理查询结果的所有文档的所需时间。 这个过期时间的参数很重要,因为保持这个游标查询窗口需要消耗资源,所以我们期望如果不再需要维护这种资源就该早点儿释放掉。 设置这个超时能够让 Elasticsearch 在稍后空闲的时候自动释放这部分资源。

    GET /old_index/_search?scroll=1m 1⃣️
    {
        "query": { "match_all": {}},
        "sort" : ["_doc"], 2⃣️
        "size":  1000
    }
    

    1⃣️保持游标查询窗口1分钟(数字+单位)。
    2⃣️关键字_doc 是最有效的排序顺序。

    这个查询的返回结果包括一个字段 _scroll_id, 它是一个base64编码的长字符串 (“scroll_id”) 。 现在我们能传递字段 _scroll_id_search/scroll 查询接口获取下一批结果:

    GET /_search/scroll
    {
        "scroll": "1m", 1⃣️
        "scroll_id" : "cXVlcnlUaGVuRmV0Y2g7NTsxMDk5NDpkUmpiR2FjOFNhNnlCM1ZDMWpWYnRROzEwOTk1OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MTA5OTM6ZFJqYkdhYzhTYTZ5QjNWQzFqVmJ0UTsxMTE5MDpBVUtwN2lxc1FLZV8yRGVjWlI2QUVBOzEwOTk2OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MDs="
    }
    
    

    1⃣️注意再次设置游标查询过期时间为一分钟。

    这个游标查询返回的下一批结果。 尽管我们指定字段 size 的值为1000,我们有可能取到超过这个值数量的文档。 当查询的时候, 字段 size 作用于单个分片,所以每个批次实际返回的文档数量最大为 size * number_of_primary_shards

    :注意游标查询每次返回一个新字段_scroll_id。每次我们做下一次游标查询, 我们必须把前一次查询返回的字段_scroll_id 传递进去。 当没有更多的结果返回的时候,我们就处理完所有匹配的文档了。
    ElasticSearch5.0以上版本的这两个查询接口为POST请求。

    异常SearchContextMissingException
    SearchContextMissingException[No search context found for id [568]]
    原因scroll设置的时间太短已经超时了,或者上次的请求返回结果中没有_scroll_id字段。

你可能感兴趣的:(ELK)