elasticsearch 单索引 6T 20亿 数据搜索实战与优化深度思考

  我负责公司的检索平台的开发兼运维工作。

  我们的场景是对互联网上的设备数据进行检索。数据量大概有20亿,对应的存储量大概有6T(不带副本的情况下)。单条数据会有上百个字段,用来刻画网络设备画像。

  我们有比较特殊的需求:

  • 我们有频繁更新的需求,每天几千万,甚至上亿。
  • 我们并不能做根据时间的滚动索引。因为后进的数据需要把前边的数据做覆盖。所以就没有办法做索引的生命周期管理。
  • 我们有频繁的聚类搜索的需求。
  • 我们想要基于这些数据,做到普通检索1秒以内,聚类检索3秒以内。

写这篇文章的目的并不是给出解决方案,而是我希望通过提出问题,并提供思路的方式,来摸索问题的答案。当然有能力的人看到这篇文章,欢迎在评论区给出好的意见。

我的疑惑和问题 

第一个问题是关于集群规划的问题

对于上述的需求,6T数据,20亿条,来检索。

  1. 到底多大的集群规模能够承载这些数据?
  2. 我们需要多少个节点?
  3. 我们需要什么样的硬件资源,多大的内存空间?
  4. 每个节点我们分配多少个CPU核心更好?
  5. 如果将这些数据都放在同一个索引,如果做才能达到普通检索1秒以内,聚类检索3秒以内的要求?

   分享一个真实的案例:

  我先说一下我对集群规划的理解(查看的官方文档,以及网上多数的集群规划相关的文章)。关于节点数,一个节点能够承载的分片数,取决于堆内存的大小,公式计算是,1G堆内存不超过20个分片。而单个分片大小,理论值是 20G - 40G性能最好。按理说,我6T的数据 = 6144G ,如果每个分片30G,那么6144/30 = 205个分片。 我的一个节点是64G内存,我分31G给堆内存。理论上来说,31能够支撑的分片数是  600个。按照这么算的话,我这么多数据一个节点就够了。

  然而实际上并不是,我并没有把这些数据只用一个集群来分配。我的测试集群是 512G内存,48核心10T固态硬盘的配置(阿里云数据中心的人说,es的单个节点最佳配置是 16个核心,64G内存,2T固态硬盘。这被多数人认可,但是我现在认为es的集群总的核心数,应该和单个索引的最大分片数对齐,甚至能多则再多一点,因为还要有一些线程去做段合并之类的。这个是根据es的检索原理来的,实际上单个分片对es来说是最小的资源分片单元,一个分片实际上就是一个lucene实例。等我再研究清楚lucene再补一篇文章来证明)。现在想想es的工作原理,我们知道这个机器配置非常的不均衡,问题在CPU核心上。我是每台机器四个数据节点,一个master节点。我有测过我单台机器的IO在每秒2G以上。我用这三台机器组成了集群,来侧这6T的数据,得到的效果挺一般的。

  我发现检索速度和我的条件有很大的关系,当我的检索结果集超过两亿以后,集群的检索时间在5秒左右。如果检索的结果集有一两千万,那么检索时间通常在一秒左右,不超过两秒。但是聚类分析通常要在 5秒左右。

  另外关于这个问题,一个es社区的朋友在看了这篇文章以后给了我答案,我也放在这里,供大家参考:

问题1. 关于集群规划的问题,你提到一个案例,依据是堆内存与分片数的经验公式, 1G堆内存换20个分片,我认为官方这个换算公式的作用是引导使用者正确评估特定物理设备上可最大承载的分片数,而不是用于反向的设备需求评估。即可以用这个公式来限制自己在一个确定的物理设备上最多搞几个分片,但是不能用这个公式来反推需要多少个物理设备。1G的堆可以放20个分片(这个更多的是基于集群管理的角度考虑,而不是性能的考虑),但是我们不会真的放那么多,对吧,这是一;分片也是有大小区分的,放20个小尺寸分片与20个尺寸大分片,很显然,效果也是不一样的,这是二。

评估到底要搞多少物理设备,相信我们不仅仅是满足能不能装得下的程度,更加注重的是性能问题。
对于设备的选用上,我不是很熟悉,你提到具体型号的服务器,我不敢瞎说。但是我个人的理解是ES更是一个内存消耗型服务,它对内存的需求远大于CPU,往往它的短板最先表现在内存上而不是负载上,所以我跟倾向于内存见长型设备。
我们团队之前做过一个很有意思的事情(你不要在意它的经济性),把redis植入到lucene里面,把整个es改造成一个纯内存的服务,跑在很廉价的机器上,经过一系列测试,我们评估这不是一个亏本儿买卖,至少亏得不明显。
不知道你有没有注意到官方还有另外一个经验公式,它描述的是堆外内存和数据量的关系,这个和我们关注的性能问题联系更加直接:对于日志型数据,要保证堆外内存(也就是给lucene的内存)能容纳总数据的10%; 对于业务数据,要保证堆外内存可以容纳数据总量的50%(这个具体数值我已经记不清楚了,在elastic官方博客里面有提到)。它这个公式不是强制的,只是推荐值,鉴于对性能追求的推荐,多多益善,如果我们希望拥有更好的性能,那就给它更多的内存吧

   针对这个回答,我的问题:

关于集群规划的问题,我只是提一个疑问。从集群的效果上来看,他们给的值,应该也是上限,而不是最佳性能,毕竟性能问题和业务有很大的关系。不过我觉得如果我们能够摸索出来一个实践公式,来推算es最佳性能,这可能是一件很有意思的事。我也在不停的去学习摸索es的检索原理,光看书,和官网,已经无法得到我问题的解。这里我想再提一个问题,就是你了解分片的工作原理吗,以及一次检索,对于es集群来说,分片是如何工作的。 我理解可能有一点偏差哈,我了解到的分片是一个lucene实例。对于一次不确定性检索来说,es无法固定到一个或者几个分片上去。所以基本上就是所有分片同时去工作的。如果是这样的话,那么cpu就非常有用了,我不太确定检索过程中,是不是一个分片去占用一个线程去执行操作。如果cpu不够的话,是不是就要有一个分片需要等待,也就是说分片执行检索变成了串行的,并不是并行的。所以我提出一个cpu对es的举足轻重的看法。有机会我会去做一个实验。如果你了解一次检索,分片工作原理的话,可以让我学习一下。

第二个问题是关于索引应该如何划分的问题

  像这样的大索引,我们是否有必要将数据做拆分?我们的集群是否能负载这么大的索引。

第三个问题是关于索引拆分能够提升的问题

  将索引拆分成小索引,然后拆分后的小索引设置相同的别名。这样是否能提升性能?拆分成多少个索引性能最好?

  我把特定条件的数据拆分了出来,然后做了检索的对比,比方说我一共有20亿数据,其中中国的的3亿。我把这3亿单独拆出来放一个索引。我发现去相同的搜索条件,去两个索引里边搜索,明显只有中国3亿数据的索引,搜索时间更短。这是我测过的得出的结论:大的目标搜索集,和小的目标搜索集,即使要搜的内容两个搜索集的交集是相同的,也是在小的搜索集搜索的时间更短!请记住这个结论,你可以去测试一下看看!     

  关于问题二和三 ,这个es社区的朋友在看了这篇文章以后也给了答案,我也放在这里,供大家参考

问题2+3. 关于索引划分的问题,我非常赞成将大索引拆分成多个小索引。
毋庸置疑,在一个小的数据集上操作会用于更大的性能优势。但是我的出发点不是性能上的考虑,而是集群的稳定性上的。
先说稳定性,对于一个包含庞大索引的集群,很难想象在弱网络环境下出现哪些匪夷所思的问题,甚至一个节点的意外退出或者加入,对整个集群都可能是一次灾难,如果分片感知策略、节点间数据同步配置不合适(translog+seqNo默认有效期似乎是12小时,另外还有一个size,具体多少G忘记了,谁先达到临界值谁先生效,一旦超过这个界限,就是物理文件segment的拷贝了),我们难以预料一次同步花费多长时间。而这些同步虽然在分片这个物理维度发生,却是在索引这个逻辑维度去调度的,索引的大小决定这个同步工作的量级。
再说性能,因为核心资源调度都是在分片维度的,所以真正影响性能的分片状态而不是索引状态(准确的说应该是分片状态对性能的影响是决定性的,但不是100%)。一个大索引拥有2个分片,和两个小索引但各自只有1个分片,在其他条件(硬件、数据量)相同的情况下他们的综合性能是相当的。
注意到你的那个测试,将3亿中国的数据摘出来单独索引,测试结果发现小的索引比之前更快,也非常具有说服力。对于这个实验我很感兴趣,不过我还有一点点建议,希望把那些因素剔除后,再精确地对比一下,结果会更有说服力:
(a) 在原来20亿的索引中,这3亿中国的数据分散在了多少个分片当中了,分片副本数是多少,这些分片又分布到哪些节点上了; 在新的3亿数据的索引中,有多少个分片,副本数设置了多少,使用可多少节点;
 (b) 检查一下缓存的使用情况,建议对比测试的时候注意不要触发到了缓存机制
如果你对这个案例有更进一步的测试,也非常希望你能同我分享一下新的测试结果

  我的追问:

  另外关于索引拆分的问题。我还有这个疑问,拆分后,也要花时间在结果合并上。实际上根据检索原理我们可以知道,检索最终都发生在分片上。如果物理资源不变的话,那么是不是即使拆分了,还是这么多资源。理论上来说不会有明显的性能提升。 

第四个问题:es对频繁修改并不友好,我们该如何应对频繁的更新的问题

  这个问题其实非常的致命,这也是网上很多人在问的一个问题,为什么es用着用着越来越慢?

可能性有以下几点:

  • 集群规模无法承载更多的数据量,到达极限以后,走下坡路了。这个时候我们应该去扩充集群。那么问题来了,什么时候是集群的极限承载能力,什么时候集群就开始走下坡路了?
  • 如果索引中的数据没有再增加,理论上不会说越来越慢。我们还可以对索引进行强行的段合并。
  • 如果es中数据在不断的增长,有两种情况,一种是数据不会发生修改,只是累积增长。第二种情况是我们的数据有唯一key,新增的数据会覆盖原来的数据,也就是发生了修改操作。第二种操作是非常致命的。特别是频繁的更新操作,会给集群带来非常大的负担。这和es的更新机制有关系,es在维护数据的时候有段的概念。对于已经放进去数据,不再修改,如果要有修改操作,也是把原来的数据标记删除,然后再添加一条新的数据。并没有办法把原来的数据修改了。在极差的情况下,一个段中有一半以上的数据都是标记删除的数据。并且段合并到一定程度,就永远都无法再进行段合并了(这个可以去了解lucene底层的段合并原理)。

你可能感兴趣的:(Elasticsearch,数据库优化,开发经验,elasticsearch,20亿数据下的检索,集群规划最佳实践,优化思路)