大厂案例 - 腾讯万亿级 Elasticsearch 架构实践2

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
日志服务 SLS,月写入数据量 50GB 1个月
简介: 大厂案例 - 腾讯万亿级 Elasticsearch 架构实践2

2.3 健壮性架构


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


大查询对于内存资源要求很高,所以很容易打垮单个节点的内存,进而逐步压垮集群中的每一个节点;


高并发点查询对于计算和 I/O 资源要求比较高,很容易导致 I/O 或者是计算资源的争抢,引起长尾、拒绝;


高并发的写入对于计算和内存要求比较高,可能引起拒绝、雪崩,当然大家常见的一个分布式集群里面的硬件故障、网络抖动之类的问题,也会引起服务的抖动。


ccaf6a07033743bfaf8064a29358e2a9.jpeg

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


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


2.3.1 服务限流

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


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


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


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


image.jpeg


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


   全内存的熔断限流是指,我们在请求的入口协调层以及执行层都做了内存的熔断,整体上限制每一个进程的总体内存以及每一个请求级别的内存的限制,


   另外通过引入弹性队列,我们可以一定程度上来缓解 CPU 跟 I/O 的抢占,并且通过弹性队列的支持,我们可以做到容忍一定的读写的毛刺。


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

   第一在于如何精准的统计大量中小查询的内存。首先我们在 Java 语言里可以通过 JOM 来获取到进程总体的内存,而对于部分查询,我们可以在关键路径上去统计大块的内存分配。


   第二个是服务限流,如何保证高吞吐呢?我们做的一个方式是通过平滑限流来保证,无限流情况下,整个集群的吞吐仍然接近于一个最高的水准。


   第三个是如何来避免大查询导致的 OOM,这个过程中你一定要及时地去检测。我们的一个做法,例如在聚合的过程中,我们会深入到聚合分桶的一个生成的过程中,去做一个梯度的熔断限流。



2.3.2 异常容忍


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


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


image.jpeg


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


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


  1. 对于写入的场景我们实现了一个分组路由,比如说原来写入是每个写入请求下发到 100 个分片,我们对分片进行分组,每一定的分片放在一个分组里,一次写入随机路由到一个分组内部。然后在这个分组里面,再按照数据本身的特性,去做 Hash 的路由,这样的话我们做到的情况是大大降低了扇出,可以来避免这种长尾的影响。同时不同批次的写入,写入到不同的分组里面,这样来利用整个分布式集群的能力。
  2. 对于查询方面的话,一方面我们引入了这种自适应路由。通常情况下,大多数分布式系统的实现是,一个请求会随机地选择数据副本去发送请求,我们做的一个方式是去做了近期时间窗口的统计,根据统计信息来把请求发到最合适的一个副本上。
  3. 另外我们引入了一个对冲请求,比如说一个请求花了 100 个分片,很难避免有个别分片成为慢请求、慢查询,我们做的一个方式是对于尾部的少量的副本的访问,我们可以做到发起一个对冲请求、备份请求,谁先回来,谁作为一个响应的结果,这样的话大大降低长尾的影响。



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


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



2.成本优化

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


2.1 解决方案



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


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

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



2.2 内存优化


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

c991f7e0e1a248dfbfb8124ac7aa2d42.jpeg


所以这里一个很重要的点实际上是,提高内存的利用效率,但是要注意保持住查询的性能。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 机制来保证数据的一致性。但是在日志和时序这种场景下,实际上存在着一定的不同,因为日志和时序场景下,它对于数据的时效性这种一致性要求相对较低。

5b0212eec4344c779856e74c505d00fc.jpeg



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




3.性能优化


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


3.1 解决方案

   存储层的话我们主要做的比如说时序的 Merge 来减少底层的小文件、优化 I/O,前面也说到了,这种数据上卷方面的工作来提升性能。

   执行器的方面的话,我们比如说前面提到的对冲查询、文件的裁剪等等,这里面也有部分 Patch 我们已经反馈给开源社区。

   优化器这一块方面的话,比如说,RBO(Rule -Based Optimization)的这种优化,然后分区的裁剪等。


324143b05f2b442fb8f2c87aa9897a05.jpeg


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



3.2 时序 Merge

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

8aca16e1ea1145ccab7f0b581db57623.jpeg


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


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


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




四、总结及未来规划


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



1.现状总结

31eb1dbbf4614c98885cff6e6ba224a1.jpeg


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


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



2.未来发展



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


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



3.开源协同


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


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




案例复盘


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


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

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
5天前
|
弹性计算 Java 关系型数据库
Web应用上云经典架构实践教学
Web应用上云经典架构实践教学
Web应用上云经典架构实践教学
|
25天前
|
弹性计算 持续交付 API
构建高效后端服务:微服务架构的深度解析与实践
在当今快速发展的软件行业中,构建高效、可扩展且易于维护的后端服务是每个技术团队的追求。本文将深入探讨微服务架构的核心概念、设计原则及其在实际项目中的应用,通过具体案例分析,展示如何利用微服务架构解决传统单体应用面临的挑战,提升系统的灵活性和响应速度。我们将从微服务的拆分策略、通信机制、服务发现、配置管理、以及持续集成/持续部署(CI/CD)等方面进行全面剖析,旨在为读者提供一套实用的微服务实施指南。
|
21天前
|
运维 监控 Java
后端开发中的微服务架构实践与挑战####
在数字化转型加速的今天,微服务架构凭借其高度的灵活性、可扩展性和可维护性,成为众多企业后端系统构建的首选方案。本文深入探讨了微服务架构的核心概念、实施步骤、关键技术考量以及面临的主要挑战,旨在为开发者提供一份实用的实践指南。通过案例分析,揭示微服务在实际项目中的应用效果,并针对常见问题提出解决策略,帮助读者更好地理解和应对微服务架构带来的复杂性与机遇。 ####
|
24天前
|
NoSQL Java 数据处理
基于Redis海量数据场景分布式ID架构实践
【11月更文挑战第30天】在现代分布式系统中,生成全局唯一的ID是一个常见且重要的需求。在微服务架构中,各个服务可能需要生成唯一标识符,如用户ID、订单ID等。传统的自增ID已经无法满足在集群环境下保持唯一性的要求,而分布式ID解决方案能够确保即使在多个实例间也能生成全局唯一的标识符。本文将深入探讨如何利用Redis实现分布式ID生成,并通过Java语言展示多个示例,同时分析每个实践方案的优缺点。
52 8
|
21天前
|
算法 NoSQL Java
微服务架构下的接口限流策略与实践#### 一、
本文旨在探讨微服务架构下,面对高并发请求时如何有效实施接口限流策略,以保障系统稳定性和服务质量。不同于传统的摘要概述,本文将从实际应用场景出发,深入剖析几种主流的限流算法(如令牌桶、漏桶及固定窗口计数器等),通过对比分析它们的优缺点,并结合具体案例,展示如何在Spring Cloud Gateway中集成自定义限流方案,实现动态限流规则调整,为读者提供一套可落地的实践指南。 #### 二、
45 3
|
23天前
|
负载均衡 Java 开发者
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
74 5
|
19天前
|
消息中间件 运维 安全
后端开发中的微服务架构实践与挑战####
在数字化转型的浪潮中,微服务架构凭借其高度的灵活性和可扩展性,成为众多企业重构后端系统的首选方案。本文将深入探讨微服务的核心概念、设计原则、关键技术选型及在实际项目实施过程中面临的挑战与解决方案,旨在为开发者提供一套实用的微服务架构落地指南。我们将从理论框架出发,逐步深入至技术细节,最终通过案例分析,揭示如何在复杂业务场景下有效应用微服务,提升系统的整体性能与稳定性。 ####
31 1
|
25天前
|
监控 Serverless 云计算
探索Serverless架构:开发实践与优化策略
本文深入探讨了Serverless架构的核心概念、开发实践及优化策略。Serverless让开发者无需管理服务器即可运行代码,具有成本效益、高可扩展性和提升开发效率等优势。文章还详细介绍了函数设计、安全性、监控及性能和成本优化的最佳实践。
|
21天前
|
监控 安全 持续交付
构建高效微服务架构:策略与实践####
在数字化转型的浪潮中,微服务架构凭借其高度解耦、灵活扩展和易于维护的特点,成为现代企业应用开发的首选。本文深入探讨了构建高效微服务架构的关键策略与实战经验,从服务拆分的艺术到通信机制的选择,再到容器化部署与持续集成/持续部署(CI/CD)的实践,旨在为开发者提供一套全面的微服务设计与实现指南。通过具体案例分析,揭示如何避免常见陷阱,优化系统性能,确保系统的高可用性与可扩展性,助力企业在复杂多变的市场环境中保持竞争力。 ####
36 2
|
21天前
|
消息中间件 运维 API
后端开发中的微服务架构实践####
本文深入探讨了微服务架构在后端开发中的应用,从其定义、优势到实际案例分析,全面解析了如何有效实施微服务以提升系统的可维护性、扩展性和灵活性。不同于传统摘要的概述性质,本摘要旨在激发读者对微服务架构深度探索的兴趣,通过提出问题而非直接给出答案的方式,引导读者深入
38 1