淘先锋技术网

首页 1 2 3 4 5 6 7


在这里插入图片描述

概述

Elasticsearch(ES)作为首选的开源分布式搜索分析引擎,通过一套系统轻松满足用户的日志实时分析、全文检索、时序数据分析等多种需求,大幅降低大数据时代发掘数据价值的成本。

腾讯在公司内部丰富的场景中大规模使用 ES,同时联合 Elastic 公司在腾讯云上提供内核增强版的 ES 云服务。大规模、丰富的实践场景,反推着腾讯在 ES 内核的稳定性、成本、性能等方面不断的进行演进

腾讯通过执行引擎优化、存储重构、线性扩展等一系列技术方案,对原生 ES 内核在高性能、低成本、可扩展性等方面进行了深入优化,目前单集群规模达到千级节点、万亿级吞吐。

提纲

  1. ES 在腾讯的广泛应用:从电商级搜索到万亿级时序数据处理
  2. 万亿级存储系统的架构设计
  3. 电商级搜索服务的平台建设
  4. 开源社区贡献及未来探索

益处

  1. 万亿级存储系统的性能 及 成本设计经验;
  2. 架构大规模、线性扩展的分布式系统的实践经验;
  3. 开源社区共建的经验。

在这里插入图片描述


正文

今天给大家分享的主题是腾讯万亿级 Elasticsearch 架构实践,一方面和大家去介绍一下 Elasticsearch,另一方面来和大家分享分布式系统设计过程中的这种创新实践的经验。

主要内容包含四部分:

  1. 一部分是 Elasticsearch 的简介
  2. 第二部分是技术挑战
  3. 第三部分是架构实践方面的工作
  4. 第四部分是一个收尾总结

一、Elasticsearch 简介

首先我们来看一下 Elasticsearch 的简介,Elasticsearch 也称为 ES,它是一套高性能分布式的存储系统,它运用了三个领域。

0. 应用领域

在这里插入图片描述

搜索引擎

比如说我们常见的 App 搜索、站内搜索、电商搜索等,它基于内部的倒排、索引、各类分词插件来满足大家的搜索需求。


可观测性

什么是可观测性呢?

是大家在运营开发过程中的日志、监控、应用性能这些基本上覆盖了应用开发过程中的所有数据,并且 ES 提供了完整的解决方案,从日志的采集、分析、可视化这些工作它都包含了,方便大家来使用。而且正因为 ES 覆盖了日志这个场景,日志体量非常大,带动了 ES 的快速发展。


安全检测

ES 提供了 SIEM,然后 Endpoint Security 从集中式和终端两个维度来帮助大家做安全检测和防御,这是 ES 使用的三大场景。


发展现状

ES 在现在发展得也非常快

  • 首先近六年成为 DB-Engines 上排名第七的一个系统
  • 然后在搜索领域里面常年排名在第一位
  • 开源日志实时分析方面也处于一个首选的地位
  • 开源状态方面有 51k 的 Star,应用状态下载方面的话有 4 亿+,而且和多个云厂商都有合作。

1.系统架构

然后我们来看一下 ES 的系统架构。

在这里插入图片描述

集群架构

首先是集群架构,ES 的集群架构是一个典型的中心化的架构,集群中包含主节点,通过分布式一致协议来保证 Master HA,并且控制着整个集群,数据节点用来提供数据的存储和访问,数据节点是线性可扩展的。

然后是物理的数据模型,物理数据模型上,ES 实际上是一个文档型数据库,它有类似关于数据库的表的结构,叫做 Index,Index 可以划分为 Shard,Shard 包含多个副本,打散到整个集群中去来保证数据的可扩展性。

另外,ES 集群中的副本动态可调,来满足大家对可用性和成本之间的一个平衡。


物理数据模型

在这里插入图片描述

然后是物理数据模型,物理数据模型大家写入 ES 中的数据,在一个 Shard 内部是怎么存储的呢?

我们来看一下,ES 实际上是一个 LSM Tree 架构的系统,写入的数据在底层会生成一个个小树,每一个小树 ES 叫做 Segment.

Segment 里面包含三类典型的数据结构,

  • 一类是行存
  • 一类是列存
  • 最后一类是倒排索引

倒排索引的作用是用于条件过滤,比如说大家去做一些查询的时候,需要用索引来加速访问,比如说我们对 title 这个字段去建立索引,那么它会在写入的时候去进行分词,不可存在于 1、2 号文档中,es 存在于 1 号文档中,这样的一个过程是属于倒排索引。

行存储用来存储原始数据,用来返回用户可以看到的原始的日志监控这些信息,列存用于排序和聚合,这样的话大家在聚合的过程中可以只读取必要的列来加速性能,这是 ES 的一个底层的物理的数据模型。


查询

然后在查询层面的话,其实 ES 提供的是一个典型的不可查询的模式,就像我们搜索引擎一样,我们可以去查 title 包含 book,content 包含 es 这样的一本书,那么 es 就在里面去按照倒排索引,去找到满足条件的文档 ID 的集合,然后再对这部分文档 ID 去做处理,这是我们看到的整个系统架构方面的一个介绍。


2.腾讯应用现状

在这里插入图片描述

ES 在腾讯内部有非常广泛的应用环境,包含公有云、私有云和内部云。

  • 公有云环境的话,它其实包含了大量的中小用户不同的使用场景,实际上可以把 ES 的边边角角每个地方都打磨的很成熟,
  • 而内部云的话会有这种超大的集群,超高的压力,可以帮助我们去发现 ES 的一些潜在瓶颈、潜在的需求。
  • 而私有云的场景下,它其实是由于网络隔离的原因,需要大家去做到标准化的交互、自动化的运营,所以对 ES 的自动化的运营是一个很好的打磨的状态。

下面我们来看看,ES 在具体几个应用领域下在腾讯的应用情况。

搜索领

在这里插入图片描述

首先是搜索领域,搜索领域里面,我们包含三类典型的应用,一个是电商产品的搜索,然后文档的搜索,然后是应用市场里面 App 的搜索类似这些应用场景。

搜索场景非常明显的特点是:

  • 首先要求高性能,你必须要有十万级的 QPS、10ms 级的平响;
  • 第二个是强相关,搜索的结果必须和用户的意图高度匹配,相当于是用户如果搜索一个苹果,可能意味着是苹果或者是苹果手机,但是你不能返回用户橘子;
  • 第三个是高可用,用户在搜索场景下要求非常高的可用性,通常都有 4 个 9,马上双 11 到了,每一个电商其实都不能够接受自己的服务挂机一个小时,所以对高可用要求非常高。

ES 是一个轻量级的、垂直化的搜索的解决方案,这样的话大家可以基于 ES 来构造各种电商级的搜索应用


日志实时分析

在这里插入图片描述

其次我们来看看 ES 的第二类典型的应用场景,日志实时分析,这也是 ES 应用最多的场景。

我们可以基于系统的日志去做一些服务质量的监控,基于业务状态的日志去做一些运营的管理,基于用户行为日志来做用户画像分析、运营分析这些东西,然后在日志场景下,它有一些很明显的特点。

时序数据

在这里插入图片描述

第三块的话我们来看一下时序数据的应用,这也是腾讯在内部发起的一个非常大的应用场景,典型的比如说包含 Metrics 的应用,应用性能的监控,还有最新的物联网数据这一方面。

时序场景的一些特点是这样

  • 它要求非常高的吞吐,所以对 CPU 资源消耗比较高;
  • 第二个是它要求比较高的查询性能,比如说大家看到通常画的监控曲线,通常可能是要求十毫秒级或者几十毫秒级要画出来,你不能拖太久;
  • 第三个是灵活、多维度的分析,讲到监控,讲到这种物联网数据,很明显是需要这种灵活的多维分析的能力。

而 ES 相比于专用型的这种时序数据库的话,它很明显的一个特点在于,它实际上可以实现类似这种专用数据库的压缩比、性能,同时简化用户的技术栈,来降低用户的运营成本、资源成本这些方面的东西。


二、技术挑战

第二部分来看看我们在这种大规模的应用环境下产生的技术挑战。

1.可用性

首先是可用性,可用性是所有分布式系统的共性问题,任何一个系统首先面临的都是可用性问题,抛开可能性去谈成本性能其实都是没有意义的,所以我们来看一下可用性方面的问题。

可用性方面,开源的 ES 早期实际上是很不稳定的,只有 2 个 9 的可用性,运营压力也非常大。

我们从整个社区的开发状态也可以看出,ES 的开发节奏非常快,但是 Bug 也相对较多一点。

在这里插入图片描述

从整个可用性问题的分类来看,我们可以看做

  • 第一个是架构设计上面的不足,不管是这种超多的分片,然后超高的读写压力,或者是分布式集群里面的负载均衡,这些东西其实都属于架构设计这一块的内容。
  • 第二部分是类似于安全攻击、误操作、自然灾害这些场景下,导致的数据丢失、网络分区这种容灾方案方面的东西。
  • 第三块是比如说分布式死锁,一些 Master 任务饿死,在这些边缘场景或者是新特性开发过程中,引入的一些内核缺陷方面的问题。

这里面其实也有一个针对 ES 的玩笑,去年或者是更早一段时间里面,社区里面经常暴露出数据被劫持、数据被误删的一些场景,实际上典型的就是 Mongo 和 ES,所以这里面对可用性的要求非常高。


2.成本

第二块的技术挑战是在于成本方面,典型的代表是日志和时序场景

日志和时序场景下,通常可以达到百万级甚至千万级的 TPS,随着时间的积累,存储成本可以达到 PB 级,非常高。而大家能够明显的感觉到日志和监控场景下,它的这种价值相对较低,所以整体上给大家带来一个非常明显的矛盾就是,你资源消耗非常高,但是业务价值相对较低。

在这里插入图片描述

我们来分析一下资源到底消耗在哪里。我们对于我们线上 Top 的大客户进行一个统计分析的时候发现,Top 的客户里面有 45% 左右的用户会保留 15 天的数据,40% 往上的用户会保留更久的数据,以 15 天为样例的话,我们来看看资源消耗的一个情况。

通常对于这类场景以单机模型为例,我们预计的是单机,需要的计算资源是 12Core,存储资源是 30TB,内存资源是 600GB,网络是 2GB 每秒

从这个比例来看内存的瓶颈最为明显,超过了现有主流机型的一些配置,也是成本最高的地方。其次是存储成本,存储成本也非常高,大家可以看到是 30TB 的存储,而且随着时间的增长存储成本是线性增长的。最后是计算方面的一个成本,大家又会发现对于日志和时序的场景下,历史数据访问其实相对较少,对于这种场景下,大家又会感觉到我访问较少,还有这么高的成本,压力非常大。


3.性能

第三个我们来看一下性能方面的挑战,主要代表的场景是搜索场景

搜索场景大家有一个很明显的情况是,大家通常是要求十万级的吞吐、10 秒级的 QPS,另外要求低毛刺

低毛刺的要求就是主要在于,比如说搜索场景 99.5% 的查询要小于 100 毫秒,这样来保证好的用户的体验。第三个是高吞吐,你要达到一个十万级的 QPS 来满足这种大中型的电商搜索的需求,这是这三方面的需求。

在这里插入图片描述

但是用户直接使用开源 ES 的话,会遇到一些问题。

  • 首先在于性能方面,大家会因为随机 I/O、Scan 执行速度不佳这些东西会导致延时比较高。
  • 第二个方面的话,因为硬件异常、后台资源的抢占、GC 网络抖动这些不可控的因素,导致长尾非常明显
  • 第三个是吞吐达不到预期,首先单节点的吞吐已经有了上限,并且你集群的扩展性并没有达到比较好的一个线性扩展的水平

所以这三个是我们的主要的挑战。


三、架构设计实践

接下来我们来看看,在针对这些挑战我们做的一些架构实践方面的东西,其实里面也包含了很多分布式系统设计过程的一些共性的问题。

1.可用性优化

首先我们来看看可用性问题,可能性问题的话其实非常丰富,我们先看一下整体的解决方案,然后后面有部分内容会给大家展开来介绍。

1.1 解决方案

可用性问题,首先在架构设计方面,ES 在架构设计方面对可用性的考虑还不是特别足,因为它是一个新系统逐步地发展起来,

  • 第一个可用性很明显的在于 ES 集群扩展性方面的可用性问题,所以我们对这块可用性的方面的东西做了支持十万级的表、百万级的分片。
  • 然后建造性方面的话,我们去支持压力过载、硬件故障这些场景下面集群的可用性的提升。
  • 第三个是集群均衡,在集群内部多节点、节点内部多盘之间,做一个比较好的压力均衡。

在这里插入图片描述

此外我们还会通过容灾方案缺陷修复这些提高整个服务的可用性,最终的结果是我们的可用性从 2 个 9 提升到目前的 3 个 9、1 个 5,整个业务发展了 10 倍,但是运营压力保持不变。


2.2 集群扩展性

下面我们来展开介绍一下集群可用性方面的一些具体的工作。

首先是集群扩展性方面的内容,一是分布式的扩展瓶颈,其实 ES 在扩展性这块的瓶颈在社区里面反馈得比较明显,一方面是分片的这种扩展,当它达到 3 万分片的数,通常去做一次管理操作,一定要 15 秒的一个耗时。而当它集群规模达到 100 个节点的时候,可能会出现一些不稳定的因素。

在这里插入图片描述

而在腾讯内部的一些大规模的使用场景下,这些使用上限瓶颈很容易被触发到,所以我们需要去解决典型的具体的触发的现象是,

  • 一个是比如说 Master 堵塞,因为元数据变更慢导致集群创建分片、创建表、删除表、集群均衡、节点的剔除这些所有的工作,其实都处于一个卡顿的状态,Master 就是几乎处于一个崩溃的状态。

  • 另外一个是建表导致写入拒绝,ES 和传统的数据库不太一样,ES 的使用方式是在写入的时候进行建表,那么如果建表卡住就会导致写入请求堆积在内存里面,进而触发拒绝。按照社区的一个建议,大家通常可能采用的方式是拆分成大量的中小集群来运维,但这种方式的缺点在于运营成本高,而且资源利用率上不去。

为什么 ES 会有这么强的一个扩展性的瓶颈,其实跟 ES 的设计有关,ES 通过元数据的管理来实现最终整个集群的管理。我们以建表的过程为例,来帮助大家来分析理解一下,为什么 ES 会有这么大的扩展性瓶颈。
在这里插入图片描述

  1. 建表请求发给集群的 Master 节点,Master 节点接收到这个请求之后,首先做的一个工作是拷贝原来的元数据状态,全量拷贝,生成一份新的元数据状态,并且在元数据状态上去帮助大家分配分片。接着是去产生元数据的 Diff,这个 Diff 是用于后续元数据同步的分发,这个过程实际上是新版元数据跟老版元数据的全量的对比。

  2. 紧接着是一个两阶段的提交过程,这个大家都很熟:第一阶段是分发,所有节点接收到这个 Diff 之后,会拷贝老的元数据生成新的元数据;第二阶段是提交,节点接收到提交请求之后,会去对比新旧版本的元数据,一个全量对比的过程,来做变更的应用。所以最终大家看到的一个状态是,整个集群的元数据变更过程中,涉及到大量的元数据的拷贝、遍历,所以限制了 ES 集群的一个元数据扩展能力

  3. 另外在两阶段提交的时候,它要求所有的节点响应或者是等到超时,所以这也限制了集群的扩展能力,怎么去解决这方面的问题,我们也做了很多思考。

业界实际上对于集群的扩展性这块的话有两种典型的实现,一种典型的实现是中心化架构,类似 BigTable 和 Kudu 这种产品,它的实现是集群里面包含 Master,Master 通过分布式一致协议来保证 HA。

在这里插入图片描述

然后集群的整个管理的过程是通过任务的方式来进行,Master 里面会有这种异步任务去关注元数据的变化,元数据产生变化,生成任务下发给指定的数据节点去执行,这样的方式效率高扩展性好。大家可能会担心这种设计方式对中心节点 Master 的瓶颈比较高,实际上 Master 很多工作可以卸载给数据节点来完成,来降低整个 Master 的压力。

第二类典型的方案是,以 Cassandra 和 Dynamo 为代表的这种对等架构,整个集群里所有的节点对等,每个节点都保留了全量的信息,然后集群的变更是通过集群内部的元数据的同步来完成,两两节点之间互相通信,最终集群的状态达到一致。这种方案的一个好处在于,架构是比较简单清晰,但是缺点在于由于这种元数据同步的收敛比较耗时,所以通常的一个情况大家看到的是,这种方案的情况下集群的扩展能力不优。

我们基于 ES 的一个实现的方案是这样,因为 ES 本身是一个偏中心化的架构了,所以我们可以去做的一个事情是:

  1. Master 仍然通过分布式一致协议来保证 HA,然后我们利用元数据的 Diff 来替代传统中心化架构中的任务,这样的话元数据 Diff 起到这个作用。所有的元数据的变更、生成、应用这些过程都基于 Diff 来完成,大大提高了元数据的扩展能力。

  2. 其次对于元数据提交的过程,我们实际上不要求所有的节点都响应或者超时,只要求大多数,这样来提高节点的扩展能力。所以通过这样的一个方案,我们既实现了中心化架构中,这种可扩展性强、效率高,同时兼容了开源 ES 的这些东西。

  3. 但是这种实现里面有几个注意点,一个注意点在于怎么去实现元数据多版本,我们实现的方案是 Copy On Right 的模式;第二个注意点是在于,既然你没有去等待所有节点的提交,那么元数据的路由信息需要去控制;第三个是元数据整体的损坏的预案。

2.3 健壮性架构

接下来我们来看看在集群健壮性方面的架构,前面是讲的扩展性,下面我们来看看健壮性。ES 是一个典型的两层的查询结构,整体上来看的话,ES 的查询包含三大类:大查询、高并发的点查询、高并发的写入

  • 大查询对于内存资源要求很高,所以很容易打垮单个节点的内存,进而逐步压垮集群中的每一个节点;
  • 高并发点查询对于计算和 I/O 资源要求比较高,很容易导致 I/O 或者是计算资源的争抢,引起长尾、拒绝;
  • 高并发的写入对于计算和内存要求比较高,可能引起拒绝、雪崩,当然大家常见的一个分布式集群里面的硬件故障、网络抖动之类的问题,也会引起服务的抖动。

在这里插入图片描述

那么从整体的资源的角度上来看的话,整个主要的瓶颈实际上内存的瓶颈最为明显计算和硬盘的瓶颈其次,怎么去解决这方面的问题?

Elastic 的设计其实默认提供了一套漏斗限流,但这漏斗限流实现的比较初级,不能满足需求。我们的实现的一个思路是,首先通过服务限流来保证整个集群的稳定,然后通过一个异常容忍的这种架构设计来提供稳定的服务,平响保持比较平稳。

2.3.1 服务限流

下面我们来看看健壮性架构方面的服务限流,用来保证集群稳定性方面的工作。

其实在服务限流这块,业界典型的实现方案有两类:

  • 一类是类似代理这种限流方式,在代理里面通过规则引擎去限流,这种方式比较适合高并发点查询的模式,适用于微服务这种场景,但是对于 ES 这种大查询的场景就不太满足。

  • 第二类是在大数据场景下,大家通常看到的以 MapReduce 或者 Hadoop 场景下为代表的任务容器的方式的限流,以 Yarn 来实现,每一个任务在执行前会去生成一个容器,在容器内部去执行这个任务。这种方式的好处在于,可以精准地控制每一个任务的资源占用,不至于导致影响其他的请求。 但是问题在于整个容器的分配非常耗时,对 ES 里面的小查询不太适合。另外一个是这种方式,对计算资源的利用率通常不高。

在这里插入图片描述

所以我们在 ES 里面实现了一套自己的方案,这套方案的主要的设计逻辑是全内存的熔断限流,再加上弹性漏斗

  • 全内存的熔断限流是指,我们在请求的入口协调层以及执行层都做了内存的熔断,整体上限制每一个进程的总体内存以及每一个请求级别的内存的限制,
  • 另外通过引入弹性队列,我们可以一定程度上来缓解 CPU 跟 I/O 的抢占,并且通过弹性队列的支持,我们可以做到容忍一定的读写的毛刺。

但这种实现方案有一定的关键点,

  • 第一在于如何精准的统计大量中小查询的内存。首先我们在 Java 语言里可以通过 JOM 来获取到进程总体的内存,而对于部分查询,我们可以在关键路径上去统计大块的内存分配。
  • 第二个是服务限流,如何保证高吞吐呢?我们做的一个方式是通过平滑限流来保证,无限流情况下,整个集群的吞吐仍然接近于一个最高的水准。
  • 第三个是如何来避免大查询导致的 OOM,这个过程中你一定要及时地去检测。我们的一个做法,例如在聚合的过程中,我们会深入到聚合分桶的一个生成的过程中,去做一个梯度的熔断限流。

2.3.2 异常容忍

前面介绍完了集群健壮性架构方面的东西,下面我们来介绍一下异常容忍方面。集群的健壮性架构能保证集群的稳定可靠,而异常容忍方面能让我们在一个不可靠的集群里面提供可靠的服务

具体的一个线上的问题的样例,比如说在日志和时序场景下,这种百万级 TPS 的场景容易触发拒绝,而我们观察的一个现象是,集群的资源利用率并不高。另一个是对于搜索等场景十万级 QPS,我们观察到,长尾查询非常明显,有很多查询达到了秒级甚至更高的一个标准,对用户体验非常不好,这个原因是什么?

在这里插入图片描述

其实在于分布式场景下,一方面异常难以消除,包含机器异常、网络的抖动、GC、后台任务对资源的抢占这些等等,其实都是分布式系统里面的异常。而对于这些异常的消除是很难彻底性去解决的,对于分布式系统里面高扇出的,分布式的读写会放大异常的影响,比如说我们一个请求访问到 100 个分片,只要其中一个分片是慢节点,那么整个请求都需要等待长尾,导致的服务的体验比较弱。

我们在这里做的一个工作是,

  1. 对于写入的场景我们实现了一个分组路由,比如说原来写入是每个写入请求下发到 100 个分片,我们对分片进行分组,每一定的分片放在一个分组里,一次写入随机路由到一个分组内部。然后在这个分组里面,再按照数据本身的特性,去做 Hash 的路由,这样的话我们做到的情况是大大降低了扇出,可以来避免这种长尾的影响。同时不同批次的写入,写入到不同的分组里面,这样来利用整个分布式集群的能力。

  2. 对于查询方面的话,一方面我们引入了这种自适应路由。通常情况下,大多数分布式系统的实现是,一个请求会随机地选择数据副本去发送请求,我们做的一个方式是去做了近期时间窗口的统计,根据统计信息来把请求发到最合适的一个副本上

  3. 另外我们引入了一个对冲请求,比如说一个请求花了 100 个分片,很难避免有个别分片成为慢请求、慢查询,我们做的一个方式是对于尾部的少量的副本的访问,我们可以做到发起一个对冲请求、备份请求,谁先回来,谁作为一个响应的结果,这样的话大大降低长尾的影响。

最终的实现的效果是对于写入,我们的 TPS 提升了一倍,资源利用率有稳步的提升;对于查询来说,我们的毛刺降低了 10 倍,降低到 100 毫秒以内

其实这里是提供了我们在不可靠环境下,去构建可靠服务的一个经验


2.成本优化

介绍完可用性之后,我们来看看成本方面的优化。

2.1 解决方案

在这里插入图片描述

成本方面我们的主要瓶颈前面介绍过,一方面在于内存、存储、计算这几个维度,所以我们做的一些工作是

  1. 首先在内存层面我做的内存的优化,提高内存的利用效率;
  2. 其次,对于硬盘存储方面,我们做了冷热分层,数据上卷这些东西来降低用户的成本;
  3. 在计算方面的话,我们通过日志即数据库这种方案来降低写入的开销,同时通过分组路由来提高资源的利用率;
  4. 当然我们还通过一些资源调度方面,多租户、弹性伸缩这些东西来提高整体的资源利用率情况。

最终的实现的效果是,我们在日志场景下仅通过软件架构可以降低成本 70%,结合这种硬件选型、资源调优这些,可以降低一个数量级的成本。


2.2 内存优化

下面我们来展开给大家介绍一些成本方面的优化工作。第一个是内存方面的优化,ES 的内存消耗很高,这主要是在于 ES 早期是做搜索场景的,所以它非常考虑性能,把索引都常驻于内存。而在时序跟日志场景下这就出现了不同,历史数据很少访问,而且部分字段可能也比较少访问,把所有的索引都放在内存里面,数据量这么大,实际上对内存的开销非常高。

在这里插入图片描述

所以这里一个很重要的点实际上是,提高内存的利用效率,但是要注意保持住查询的性能。ES 社区在这方面做了一定的工作,它做的一个方案是,写入查询的过程中,索引是按需加载的,放置在系统缓存里。但是这种方案的不足点是在于,系统缓存它不会区分索引跟数据,所以当出现一个大查询把系统缓存冲刷之后,整个后续会引起查询的抖动。

我们在这里实现了自己的方案,采用独立的 Cache 来完成这个工作,一方面我们做了精准的 Cache 命中率方面的优化,比如说主键索引不访问,我们可以不放入 Cache,Merge 的过程中,我们会主动地把历史数据的索引剔除掉,来提高整个索引的内存利用效率。其次对索引的使用性能这块,我们也做了优化。比如说多次 Cache 的查找,可以在多个查询之间进行共用,然后对于 Cache 的放置位置,我们把它移到了堆外,在 Java 语言里放置堆外可以降低 Java GC 的影响,提升性能。

所以最终的效果是,我们内存利用率提升了 80%的同时,性能基本上保持不变,Cache 的命中率在 99%+。主要还是原生的 ES 在内存利用率这块使用得相对较弱。


2.3 数据上卷:计算置换存储

第二块是成本优化在于数据上卷。其实在监控这种场景下,这种需求很自然,就是对历史数据、监控场景需要保存半年以上的数据,同时又要求查询性能很高,怎么去做呢?典型的一个做法是通过预计算来换取成本,我们可以把数据提前按照时间,或者是按照其他的维度做了一个聚合,聚合之后成本大幅下降。

在这里插入图片描述

这里面我们介绍一些典型的方案的对比,一些典型的方案比如说依赖于这种 Hadoop 的离线计算这种方式,依赖复杂而且计算成本高;其他的一些方式,比如聚合查询,聚合查询在大量这种基数的情况下容易导致内存的打爆;再有比如说 Merge 任务这种方式,底层在 Merge 的过程中把数据的力度,完成这种数据上卷的一个计算的过程,但这种方式存在一个冗余表的存储。

我们采用的方案是,对于 ES 中的底层数据采用流式任务调度的方式,去进行多路归并来完成这个过程,不存在冗余表的写入。整个计算也只有写入的 10% 内存,非常可控,而且数据的延迟相当于只有实时数据的基础上增加了 5 分钟,对用户体验非常好。

2.4 日志即数据库

日志即数据库这一块。日志即数据库实际上是一个很有创新的设计,大多数分布式系统中都存在主从副本的一个设计,主从副本多数情况下是对等的,而且副本之间通过全同步或者 Quorum 机制来保证数据的一致性。但是在日志和时序这种场景下,实际上存在着一定的不同,因为日志和时序场景下,它对于数据的时效性这种一致性要求相对较低。

在这里插入图片描述

所以我们这里可以做一个非常好的优化就是日志即数据库的概念。对于主分片我们通常情况下,分布式系统设计保持一致,而对于从分片的话,我们是写入日志来保证高可靠、周期性的从主分片,来拉取数据文件来提供读取。这样的话可以降低一倍的写入,整个写入的吞吐可以提升一倍,有非常好的一个效果。

3.性能优化

最后我们来看看性能优化方面。性能优化方面我们做的工作其实也比较丰富,从整个 ES 的存储层到执行器,到优化器都做了一些工作。

3.1 解决方案

  • 存储层的话我们主要做的比如说时序的 Merge 来减少底层的小文件、优化 I/O,前面也说到了,这种数据上卷方面的工作来提升性能。
  • 执行器的方面的话,我们比如说前面提到的对冲查询、文件的裁剪等等,这里面也有部分 Patch 我们已经反馈给开源社区。
  • 优化器这一块方面的话,比如说,RBO(Rule -Based Optimization)的这种优化,然后分区的裁剪等。

在这里插入图片描述

总体的最终的效果是,我们对于这种搜索场景,我们的性能可以提升一倍,毛刺可以降低一个数量级,同时整个集群的线性扩展非常好。

3.2 时序 Merge

这里我们展开介绍一下 Merge 策略这一块。这里我们其实在实际场景下引入了一种新的 Merge,叫时序 Merge。ES 传统的 Merge 实际上是典型的考虑大小、考虑效率的 Merge,会尽量把大小相似的文件放到一起,并且限制单个文件的大小上限。这种方式有一个不好的地方在于底层文件还是相对较多,有 30 个文件,所以大家分析的时候涉及到大量的 I/O。

在这里插入图片描述

然后另一个方面的话,因为底层数据文件的时间上面并不是连续的,一个文件可能包含了 1 月份整月的数据跟 3 月 1 号的数据,所以这个时候如果大家去查询 3 月初的数据的话,一个问题是底层大量文件都需要扫描,不利于裁减。开源社区里面有很多这种 Merge 方案,典型的以 LevelDB 为例,LevelDB 底层也实现了自己的 Merge 策略,或者叫 Compact 策略。数据在产生的时候放在 level 0 层,然后数据按照这种 Level 0 层,或者下面层次的文件的个数,从上层往下去 Merge,最终来实现整个数据的管理。

这种模式非常适合这种点查询或者是 QV 系统,内存中因为保存了这些文件的一定的索引信息,我们进行点查询的时候,它根据索引信息可以做快速的裁剪,最终只需要大家去扫描少量几个文件就可以返回结果。但是对 ES 实际上不太适合的,因为 ES 它通常需要去读取较多的数据,那么就涉及到底层这么多文件的扫描,小文件太多了,所以我们引入了一个时序的 Merge 的方案。

大概的思路是,**首先我们会在 ES 这种分层的 Merge 基础上引入时间序的一个概念,相当于是同一个文件内部的数据,时间上尽量是连续的,方便大家裁剪。另一个方面是我们会做冷数据的 Merge,日志、时序数据,很多天以前的数据其实不太访问了,所以我们可以把它的文件数做一个收敛来提高性能。**最终我们在时序的 Merge 的效果是,基本上对产品的性能可以提升一倍。

四、总结及未来规划

前面介绍完了这么多内容之后,我们做一个简单的总结。

1.现状总结

在这里插入图片描述

从目前的状况来看,整体上我们对 ES 的可用性、成本、性能这些方面都做了很多的优化,在整个社区里面也处于一个领先的地位。然后在与集群的管控托管这一块,其实我们也提供了一个成熟的托管平台,包含集群的管控操作、监控、巡检这种丰富的平台。

另外我们目前业务发展也达到了一个非常快速的阶段,支持了日志实时分析、全文检索、时序处理等过程,而且我们与官方的 Elastic 生态有很好的兼容。

2.未来发展

在这里插入图片描述

接下来看看未来的一个规划,其实前面说了,在日志跟时序场景下,成本和数据的价值始终是一个矛盾,所以我们会从这两个维度着手去解决。成本方面,我们会持续地从资源调度、系统架构方面持续地去优化。然后对于业务价值方面,我们采用的措施是,一方面会去和大数据生态打通,方便大家来利用大数据生态里面的数据;另一方面其实大家对数据的利用典型的划分,可以理解成数据工程类型的利用,比如说流批的处理、数据的搜索分析(搜索分析比如说大家去做一些交互式的运营分析)。

第三类是在线的 App,而在搜索分析这个领域里面包含了非常多的系统、非常多的软件,比如说搜索领域的 ES Solr,分析领域里面的 ClickHouse、Doris 等各类系统,里面的系统非常多,所以我们希望基于 ES 去扩展交互式分析领域,在一套系统里面来提供 PB 级日志的闭环的解决方案,帮助用户去进一步地挖掘数据的价值,同时减少这种维护的系统的数量,降低维护成本。

3.开源协同

在这里插入图片描述

最后一块的话,我们来看一看开源协同的部分。整个开源协同这一块,我们向社区做了大量的贡献,包含源码 Patch 上的贡献,我们是整个亚太地区 ES 贡献最大的一个团队;其次在社区活动方面,我们也积极参加社区里面的这种技术峰会 Meetup,然后技术文稿也在持续地输出。

当然对开源社区的贡献,一方面有利于社区的发展,另一方面对于我们个人、对于团队也有非常好的收益。技术层面的话,首先可以降低我们的版本维护成本,我们的分支上的功能特性相对于开源社区会保持跟进得比较紧密,随时了解开源社区的动态;另一方面人才培养这块的话,开源社区这种开放透明、高效的这种开发模式,也有利于大家培养一个好的开发习惯;在影响力方面,我们也得到了包含 Elastic 公司的 CEO 开发者的认可、社区组织的认可,也有利于我们去吸引人才,去助力产品的发展。最后还是倡议大家一起投入到这种开源社区的反馈的过程中,最终实现开源社区、企业、个人共赢的过程。

案例复盘

需要注意的陷阱是,ES 的可用性方面有些问题。一些典型的陷阱,比如说集群的扩展性,如果大家使用开源的 ES 的话,我们建议集群里面的表的数量不要超过 1 万,然后集群的节点数不要超过 100 个,来保证比较平稳的运行。当然如果大家有更高的需求的话,需要参考社区去调优,做一些最佳实践的参考,这是这一个方面的陷阱。

另一个方面的一些陷阱,如果大家在遇到一些 ES 使用方面的问题,或者是资源不足方面的一些假象的时候,尽量从机器资源这些角度层面,从根本原因上进行细致的分析,这样的话来找到问题,而避免盲目地去扩容解决问题。

在这里插入图片描述