继上文在完成了第一阶段 ES 查找引擎的搭建后,现已能够实现对千万等级的产品索引的读写恳求的支撑。现在,单机房读流量在 500~1000 QPS 之间,写流量在 500 QPS 左右。

但跟着事务的开展,问题也逐步开端露出,起源是在某次活动下线的时候,ES 集群某个机房 CPU 迅速被打满,读推迟上升,而其他机房却是正常的,之后仍然呈现了多次 CPU 暴升,多个机房的其间一个机房被打满或许一起打满的景象,可是读写流量波动却不大或许底子不及日常峰值。咱们意识到此刻呈现的便是 ES 集群的功能问题,在第一阶段当体系依靠组件不可用时,为此体系具有必定的容灾才能,暂时没有考虑事务运用姿态带来的危险,而这种危险是更可怕的,源于它随机, 毫无规律,不可操控。

在此状况下,也许大家会考虑经过扩容来处理问题,但当前状况现已是在咱们扩容后产生的问题了,所以很显着此刻扩容现已处理不了问题了。程序员常常说的几句表达危险等级的话:

  • 阶段一:不知道自己不知道(Unconscious incompetence)
  • 阶段二:知道自己不知道(Conscious incompetence)
  • 阶段三:知道自己知道(Conscious competence)
  • 阶段四:不知道自己知道(Unconscious competence)

用在此刻便是阶段一和阶段二,不知道自己不知道,所以也就无法在事务危险操控这儿发力,当问题呈现后,知道了自己不知道,但不知道产生了什么,不知道为什么会产生,不知道还会产生什么。看似充溢随机性,但实际上却是一个很严厉的问题,招商渠道对大促活动十分重要,由此也引发了咱们新的考虑,咱们做 ES 安稳性的大局视角是什么,该怎样定义和归类?这些考虑也为后续的管理供给了更好的处理思路和发现问题的视点。

管理思路

电商场景下 ES 查找引擎的安稳性管理实践

ES集群读写链路图

在管理思路中咱们仍然从体系读写两个进口下手,别离细化读和写链路应该考虑的问题和危险以及需求抵达的事务方针,下文将从详细的实施步骤进行介绍。

管理的方针是什么

管理方针有两个,别离是体系的可用性、安稳性和数据质量, 体系可用性指的是安稳供给读写才能,数据质量即确保 ES 的数据和源数据完全共同,并且推迟符合事务预期,抵达不只有数据而且是有质量的数据规范。

怎么量化方针

在量化方针中,体系可用性沿用了 ES 集群 SLA 进行衡量可用性。数据质量能够理解为数据最终共同性和数据推迟,现在咱们核心的数据包含准实时数据流,报名记载 DB->ES,产品比价经过文档数据库->ES,并需求守时更新目标。DB->ES 设定的方针是 30s 内的共同率在 99.9% 以上,经过准实时对账进行监控报警监测。

跟着产品控价越来越重要,比价的数据挑选和查询也尤为重要,文档数据库->ES 设定的是不存在超时小时等级的同步推迟,且将守时更新目标定为 T 1。

怎么抵达方针

原则:自上而下,逐层拆解,互相独立,互为补充。

电商场景下 ES 查找引擎的安稳性管理实践

优化措施

此刻回忆一下,上节咱们说到的 ES CPU 暴升问题最终是怎么处理的?实际上,咱们并没有走捷径,而是将 ES 读链路悉数梳理了一遍,剖析每次 CPU 暴升的流量差异点。之前的剖析仅仅是从 ES 集群监控上剖析不同索引的流量趋势,由于差异点太小,无法进行有效剖析。因此,咱们仍然需求先完善监控报警机制,将 ES 上层的云引擎服务的接口流量监控悉数聚合在一个监控看板上,并加入了 API /中心 RPC 层–> 数据中心 RPC 服务–> ES,然后找到了问题的突破口。咱们发现,CPU 上涨的点 Scroll 流量偏高,由于 Scroll 流量比 Search 流量更耗 CPU,因此 Scroll 流量会被打满。在清晰原因之后,咱们也就开启了 ES 功能的优化之路。

为什么 ES Scroll 流量比 Search 流量更耗 CPU?

Search 查询有数据缓存而 Scroll 没有:在Search API 中,ES 会履行查询并返回匹配的成果集。这些成果通常是直接从索引中检索的,并且在查询时或许会运用缓存来进步功能。一旦查询完成,ES 会将成果缓存在内存中,以便稍后进行排序、分页等操作。这样,在后续的恳求中,假如只需求拜访缓存中的数据,能够避免从头核算和拜访磁盘,然后减少了 CPU 的耗费。相比之下,Scroll API 在处理流量时不会运用缓存。它的工作方法是创立一个游标(Cursor),并在服务器端保护一个快照,以便在后续的恳求中能够继续从上一个恳求的方位继续返回成果。这意味着每次恳求都需求从头核算和拜访磁盘上的数据,并且不能运用缓存。这会导致更多的 CPU 核算和磁盘拜访,然后添加了 CPU 的耗费。

  1. Search 是无状态查询,Scroll 需求上下文保护:Scroll API 需求保护上下文信息,以便在后续的恳求中能够正确地返回成果。这个上下文信息或许包含游标方位、排序信息、过滤条件等。为了保持这些上下文的共同性和完整性,ES 需求在服务器端保护和更新相关的状态。
  2. 这不意味着 Scroll API 必定比 Search API 更耗 CPU。实际的 CPU 耗费还遭到多个要素的影响,包含查询的复杂性、数据量的巨细、硬件装备等,需求结合实际状况观测。

ES 查询链路管理

将 ES 不合规的 Scroll 流量悉数迁走

在某次大促活动之前,招商渠道供给的某个活动下报名记载的全量获取接口走的满是 ES Scroll 流量,根本维持在 100 QPS 水平,大多场景用于离线对账和初次数据拉取,咱们经过跟事务沟通改离线对账或许走 DB 查询等方法,把不合理的 Scroll 查询迁移走。迁移了 Scroll 恳求约 30 事务方, QPS 从 100 降到个位数,根本处理了 Scroll 场景的功能危险。

ES 慢查询管理

慢查询是一个相对的概念,不是一个绝对的概念,不是说某种查询必定是慢查询,或许某种查询必定不是慢查询,他和数据规划等要素相关性很大。大多都是由于实现方法的原因,他的慢会跟着数据规划增加而逐步显着,所以支撑亿级数据量和万级、百万级完全不是一回事,不到必定数据量,相同的实现或许也并不会产生问题。

在大规划数据场景下,慢查询的慢会越发显着,往往慢查询几十的 QPS 就能占用正常查询上千 QPS 所需求的资源。在其它流量突然添加的状况下,一般慢查询的耗时会成倍添加,也意味着它占用的资源一向得不到释放,给体系带来巨大的功能危险。下面举例说明一些观察到的 ES 慢查询:

  1. Terms 查询在每次查询的数量过大时都会导致慢查询,体系当时存在每次 Terms 查询 万个产品的场景,耗时在 1s ,产品写流量进来后,查询耗时翻好几倍,CPU 被打满。
  2. 对 Double 类型字段做 Term 查询,由于检索方法和数据结构不匹配,相同还是由于数据量过大,导致慢查询。
  3. 高区分度字段 Terms 聚合。

慢查询的躲避手段也现已相对比较成熟。能够完善慢查询的监控报警机制 在 CPU 运用率是偏高时制定合理的报警阈值。借此咱们也梳理了 ES 查询或许存在的慢查询 Case,排查其他事务危险,由此慢查询带来的 CPU 上涨问题也现已被排查处理。

Range 查询优化

缓存是提高 ES 查询功能的重要手段,假如查询缓存命中率低,则能够定向优化。ES Filter 查询的时候会缓存查询频次较高的恳求成果,可是 Range 查询的特殊点在于,假如每次查询的时刻区间不一样,会导致一向缓存,可是命中率极低,引发体系频繁 GC,然后形成安稳性问题。

电商场景下 ES 查找引擎的安稳性管理实践

优化方法:

  1. Range 查询走普通查询,不经过 Filter 过滤器缓存。
  2. 优化 Range 查询,比方指守时刻区间查询,进步分片维度恳求缓存命中率,并下降缓存频繁构建和废物收回频率。

仅查询需求的字段

在咱们的体系中就曾呈现过获取活动列表活动的装备十分大,流质变高时迅速把 CPU 打满的问题。首要由于 ES 查询默认是 query_then_fetch 模式,假如事务的索引文档比较大,每次查询都返回整个索引文档的话,那么 Fetch 的耗时就会变高,形成慢查询,或许内存被打爆的状况,所以仅查询需求的字段能够节省带宽,和磁盘拜访耗时,然后提高查询功率。

ES 写入链路管理

仅写入需求索引的字段

ES 的定位是查找和统计,所以咱们在后面的管理中也是十分慎重对待需求写入 ES 的字段,仅写入索引和统计字段,其它数据则能够回表查询,该方法能够避免索引胀大速度过快,影响查询和索引重建功率,也是更多资源的糟蹋。

Nested 索引优化

索引通常会面对父子文档相关文档这样的查询场景,有的还要求子文档能够独立查找,Nested 类型便是 ES 帮甲方处理此类问题的。Nested 其实是十分好的一个规划,功能也很优越,但它的条件是子文档不能太大,子文档深度不能太深,文档胀大相对可控,查询方法友爱,总归大数据规划运用 Nested,需求多加条件,能不用就不用,小数据规划就不用太有担负。

Nested 的查询和索引功能都稍逊于普通索引类型,通常是普通索引好几倍的资源耗费,咱们为了处理产品索引 SPU->SKU Nested 慢查询问题,以及下降索引胀大速度,经过将 ES 的 SKU Nested 索引设置为 Object 类型,并且把 SKU 维度的信息核算成果作为 SPU 字段共同供给简单查询,满足事务查询需求,这样咱们既做到了事务无损,也下降了体系压力。经过以上优化咱们的写入胀大系数下降了 20 倍左右,文档数从 40 亿减缩到了 2 亿,并经过压测佐证写入功能提高了 20% ,也不会再高频呈现该类慢查询的状况。

音讯乱序问题

RocketMQ 乱序问题:

  1. 经过 Client SDK 发送数据时,假如发送失利则会快速重试发送到其它 Queue,此刻同一个 Key 的音讯在不同的 Queue 中形成音讯乱序抵达 ES;
  2. RocketMQ 假如呈现产生 Rebalance,或许会导致同一组音讯一起给多个消费者消费,然后产生 ABA 覆盖写问题。

以上都会形成丢失更新的问题,所以需求运用 RocketMQ 来确保有序性,但也并不能抵达 100% 的作用。咱们在比价消费场景中就曾遇到问题,一个报名产品有 N 个 SKU,会别离进行站表里比价,以及自身比价成果核算,根本上都是并发进行的,这就导致多个比价成果在同一时刻抵达,其间一个音讯在写入时产生失利自动重试写入到其它 Queue,即产生了比价音讯更新覆盖的问题。在详细的处理过程中咱们规划了如下三个计划:

  1. 选用比价的 Version 乐观锁操控,选用 Script Verison 写,可是由于 Script 的写入功能不高,而且比价现在的写入流量最高 2k ,未来跟着产品量级添加会更多,所以未选用。
  2. 选用 ES 的 Version 版本号操控,写入时带上 Version 版本号,可是由于前面介绍过咱们的一条报名记载会有多个写入进口,大局 Version 版本号的形式成本太高,也未选用。
  3. ✅ 选用批量聚合消费的方法。即 FaaS 单条消费改为批量消费,依照最大音讯数或许聚合时刻消费聚合,这样能够处理单条音讯并发更新的 Case。选用该方法首先由于改动成本低,自身的 SDK 也能够支撑,能够聚焦处理问题,少数 Case 仍然运用对账 T 1 补偿的方法。

ES 资源隔离

现在咱们的 ES 集群承载着所有招商需求的 ES 索引的流量,包含活动、企划、报名实体索引等,现在的安稳性确保预期总读流量能够支撑 2000 QPS,写流量 6000 QPS。可是在压测时运用线上实在流量压测,咱们在别离压读、压写,一起压读和写,经过操控变量的方法压测时发现读流量的资源歪斜十分严重,部分节点的 CPU 运用率很高,整体压不上去。经过剖析后发现是由于不同索引的分片散布不一样导致的,所以读写流量散布不均,并且不同索引的重保等级是不一样的,介于此原因,咱们认为资源隔离能够更好的躲避危险,进步体系可用性,依据不同的分片散布特性,分配不同的 ES 集群标准,也有利于资源运用率最大化。

管理作用

  1. ES 集群资源运用状况符合预期,不再呈现 CPU 暴升、CPU 被打满、持续慢查询状况,根本处理了非预期的 CPU 增加问题,体系功能保持安稳。
  2. ES 索引文档数从 40 亿减缩到 2 亿 ,ES 写功能提高 20% ,写入 QPS 最高可支撑 1w ,功能上能够超出事务需求满足事务运用。

当然在每次活动之前,咱们也都会结合安稳性的管理 House 来剖析容质改变、流质改变、监控报警,并依据需求定向优化以上几个方面,确保每一次的体系改变都在预期范围内,把一切不确认要素变得确认。当然在未来的实践中仍需不断探究,发掘 ES 在实践才能上的更多或许性。

文章来历|字节跳动商业渠道 王丹