ES 亿级数据毫秒级性能优化?

ES 亿级数据毫秒级性能优化?

 

  1. ES 简介

  2. 问题与剖析

  3. 搜索性能优化

    1. 性能的关键:filesystem cache

    2. 三人行必有我师(性能优化)

 

 

 

1、简介


 

    ES是一个基于RESTful web接口并且构建在Apache Lucene之上的开源分布式搜索引擎。

 

    同时ES还是一个分布式文档数据库,其中每个字段均可被索引,而且每个字段的数据均可被搜索,能够横向扩展至数以百计的服务器存储以及处理PB级的数据。

 

    可以在极短的时间内存储、搜索和分析大量的数据。通常作为具有复杂搜索场景情况下的核心发动机。

 

2、问题与剖析


 

问题:

       es 在数据量很大的情况下(数十亿级别)查询一定快吗,如何提高查询效率啊?

 

剖析:

    crud 是所有的数据存储服务的基本操作,无论是 NoSQL、SQL数据库、文件存储服务...... 既然是基本操作,就有一个大家都会遇到的问题:性能。

    有朋友说,ES 本身的架构设计就是为了解决大批量的数据检索问题,他的查询一定快,我们只管查就对了。

    个人认为,他说的对, 对于一些只进行 CRUD 的朋友来说,这已经够了。但是,我们日常的工作并不是只做 CRUD,我们需要设计,设计索引,优化索引,优化查询,这就不够了。知其然,需知其所以然,直到他为什么快,直到的快的关键点(瓶颈)在哪里,才能更好的设计与使用。

    

    那么,这个问题的答案就很简单了,答案是:不一定快,或者说,肯定会有慢的时候。就像MySQL 查询一样,肯定有一个瓶颈。

 

    重点来了,瓶颈在哪里?如何优化(解决)瓶颈??

    

 

3、搜索性能优化


 

a. 性能的关键:filesystem cache


    持久化,是大部分的数据存储服务都具备的特性之一,而且,几乎所有的持久化信息最终都会落到磁盘中,ES 也不例外。

 

ES 检索快的原因有两点:

           1、磁盘存储的数据结构 - 倒排索引

           2、检索机制 - filesystem cache

 

    第一点,暂时不说,相对复杂一些,牵扯到分词内容,我们先忽略第一点。

 

 

ES 亿级数据毫秒级性能优化?

   在 ES 的查询过程中,依赖了底层的 filesystem cache 。也就是,先查  filesystem cache ,查到了直接返回;查不到时,查询磁盘文件并缓存到  filesystem cache  中,然后返回。

    看到这里,是不是感觉很熟悉,像不像 Redis - MySQL 的使用方式:先查缓存,然后查数据库,同时缓存数据。

    从此,可以看出,性能的关键就在缓存能放下多少数据。那么,缓存能放下多少数据和由什么决定呢?感觉是不是抓住了点什么,嘻嘻,由内存决定。

 

    举栗说明:假设我们有三台服务器,内存是32G ,es 的jvm heap 设置为 16G(用于计算),留给缓存 filesystem cache 的只剩下了 16G(可能还不够16G,为了方便计算,用16G 进行说明)。此时,三台机器能够缓存的数据量 =  3 * 16G = 48G,也就是说,只有这 48G 的数据查询数据快,其他的都要从磁盘文件获取,那么,性能也就降下来了。

    

    总结:要想性能好,内存必选大。至少保证达到整体数据量的某一个百分比,eg:50%。

 

    有人说,不合理啊,实际情况下我们不可能知道或者说决定机器的内存有多大的。也不能保证达到整体数据量的某一个百分比。

 

b. 三人行必有我师(性能优化)


 

    子曰:三人行,必有我师。既然都是数据存储服务,我们就可以参考同行的案例啊。

    同行给了我们三个方面的优化方式

 

数据存储优化


 

    一句话,节约 filesystem cache 的信息量,能不放 es 的就不放 es,es 只放关键数据。eg:id、name、age等。

    结合 MySQL、Hbase 、Redis 进行查询。从 ES 中获取主要数据 ID ,通过ID 查询完整信息。ES 的查询可能要 20 ms,完整信息的获取需要40ms,整个查询过程只需要 60ms。这样是不是更快了呢??

    有一个问题考虑,如果数据牵扯到了分库分表,那么我们要将路由关键字记录在 ES 的索引中。

 

冷热数据、数据预热


    

    es 可以做类似于 mysql 的水平拆分,就是说将大量的访问很少、频率很低的数据,单独写一个索引,然后将访问很频繁的热数据单独写一个索引。最好是将冷数据写入一个索引中,然后热数据写入另外一个索引中,这样可以确保热数据在被预热之后,尽量都让他们留在 filesystem os cache 里,别让冷数据给冲刷掉。

    你看,假设你有 6 台机器,2 个索引,一个放冷数据,一个放热数据,每个索引 3 个 shard。3 台机器放热数据 index,另外 3 台机器放冷数据 index。然后这样的话,你大量的时间是在访问热数据 index,热数据可能就占总数据量的 10%,此时数据量很少,几乎全都保留在 filesystem cache 里面了,就可以确保热数据的访问性能是很高的。但是对于冷数据而言,是在别的 index 里的,跟热数据 index 不在相同的机器上,大家互相之间都没什么联系了。如果有人访问冷数据,可能大量数据是在磁盘上的,此时性能差点,就 10% 的人去访问冷数据,90% 的人在访问热数据,也无所谓了。

 

    假如说,哪怕是你就按照上述的方案去做了,es 集群中每个机器写入的数据量还是超过了 filesystem cache 一倍,比如说你写入一台机器 60G 数据,结果 filesystem cache 就 30G,还是有 30G 数据留在了磁盘上。

    其实可以做数据预热。

 

    对于那些你觉得比较热的、经常会有人访问的数据,最好做一个专门的缓存预热子系统,就是对热数据每隔一段时间,就提前访问一下,让数据进入 filesystem cache 里面去。这样下次别人访问的时候,性能一定会好很多。

 

 

索引 ducument 设计


    对于 MySQL,我们经常有一些复杂的关联查询。在 es 里该怎么玩儿,es 里面的复杂的关联查询尽量别用,一旦用了性能一般都不太好。

    最好是先在 Java 系统里就完成关联,将关联好的数据直接写入 es 中。搜索的时候,就不需要利用 es 的搜索语法来完成 join 之类的关联搜索了。

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

    

分页性能优化


 

    不要做深度分页,在es 中默认查询上限是10000 条数据。如果超过一万条不返回。

    这是一个很大的坑。为什么这样呢?

    原因:分页查询的查询机制。client 请求查询100 条数据,接受请求节点(node 1)接收请求,node 1 向所有的节点索要数据,每一个节点都给 100条数据,然后由node 1 来进行排序获取,也就是拿到最终的100条数据。第一页如此,第二页也是如此。所有我们每次分页的开销是比较大的。所以慢慢的分页查询也就会越来越慢。

    

    如果需要获取深度的分页数据,可以通过 scroll API(游标查询) 来解决,这里就不多说了,下一篇文章会详细介绍,嘻嘻。

          

ES 亿级数据毫秒级性能优化?