电商场景下 ES 搜索引擎的稳定性治理实践

news2024/9/27 15:23:09

继上文在完成了第一阶段 ES 搜索引擎的搭建后,已经能够实现对千万级别的商品索引的读写请求的支持。目前,单机房读流量在 500~1000 QPS 之间,写流量在 500 QPS 左右。

但随着业务的发展,问题也逐渐开始暴露,起源是在某次活动下线的时候,ES 集群某个机房 CPU 迅速被打满,读延迟上升,而其他机房却是正常的,之后仍然出现了多次 CPU 暴涨,多个机房的其中一个机房被打满或者同时打满的情形,然而读写流量波动却不大或者根本不及日常峰值。我们意识到此时出现的就是 ES 集群的性能问题,在第一阶段当系统依赖组件不可用时,为此系统拥有一定的容灾能力,暂时没有考虑业务使用姿势带来的风险,而这种风险是更可怕的,源于它随机,毫无规律,不可控制。

在此情况下,也许大家会考虑通过扩容来解决问题,但当前情况已经是在我们扩容后发生的问题了,所以很明显此时扩容已经解决不了问题了。程序员经常说的几句表达风险等级的话:

  • 阶段一:不知道自己不知道(Unconscious incompetence)

  • 阶段二:知道自己不知道(Conscious incompetence)

  • 阶段三:知道自己知道(Conscious competence)

  • 阶段四:不知道自己知道(Unconscious competence)

用在此时就是阶段一和阶段二,不知道自己不知道,所以也就无法在业务风险控制这里发力,当问题出现后,知道了自己不知道,但不知道发生了什么,不知道为什么会发生,不知道还会发生什么。看似充满随机性,但实际上却是一个很严肃的问题,招商平台对大促活动非常重要,由此也引发了我们新的思考,我们做 ES 稳定性的全局视角是什么,该怎么定义和归类?这些思考也为后续的治理提供了更好的解决思路和发现问题的角度。

治理思路

ES集群读写链路图

在治理思路中我们仍然从系统读写两个入口入手,分别细化读和写链路应该考虑的问题和风险以及需要达到的业务目标,下文将从具体的实施步骤进行介绍。

治理的目标是什么

治理目标有两个,分别是系统的可用性、稳定性和数据质量,系统可用性指的是稳定提供读写能力,数据质量即保证 ES 的数据和源数据完全一致,并且延迟符合业务预期,达到不仅有数据而且是有质量的数据标准。

如何量化目标

在量化目标中,系统可用性沿用了 ES 集群 SLA 进行衡量可用性。数据质量可以理解为数据最终一致性和数据延迟,目前我们核心的数据包含准实时数据流,报名记录 DB->ES,商品比价通过文档数据库->ES,并需要定时更新指标。DB->ES 设定的目标是 30s 内的一致率在 99.9% 以上,通过准实时对账进行监控报警监测。

随着商品控价越来越重要,比价的数据筛选和查询也尤为重要,文档数据库->ES 设定的是不存在超时小时级别的同步延迟,且将定时更新指标定为 T+1。

如何达成目标

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

优化措施

此时回顾一下,上节我们提到的 ES CPU 暴涨问题最后是如何解决的?实际上,我们并没有走捷径,而是将 ES 读链路全部梳理了一遍,分析每次 CPU 暴涨的流量差异点。之前的分析仅仅是从 ES 集群监控上分析不同索引的流量趋势,由于差异点太小,无法进行有效分析。因此,我们仍然需要先完善监控报警机制,将 ES 上层的云引擎服务的接口流量监控全部聚合在一个监控看板上,并加入了 API /中间 RPC 层--> 数据中心 RPC 服务--> ES,从而找到了问题的突破口。我们发现,CPU 上涨的点 Scroll 流量偏高,因为 Scroll 流量比 Search 流量更耗 CPU,因此 Scroll 流量会被打满。在明确原因之后,我们也就开启了 ES 性能的优化之路。

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

  1. Search 查询有数据缓存而 Scroll 没有:在Search API 中,ES 会执行查询并返回匹配的结果集。这些结果通常是直接从索引中检索的,并且在查询时可能会使用缓存来提高性能。一旦查询完成,ES 会将结果缓存在内存中,以便稍后进行排序、分页等操作。这样,在后续的请求中,如果只需要访问缓存中的数据,可以避免重新计算和访问磁盘,从而减少了 CPU 的消耗。相比之下,Scroll API 在处理流量时不会使用缓存。它的工作方式是创建一个游标(Cursor),并在服务器端维护一个快照,以便在后续的请求中能够继续从上一个请求的位置继续返回结果。这意味着每次请求都需要重新计算和访问磁盘上的数据,并且不能利用缓存。这会导致更多的 CPU 计算和磁盘访问,从而增加了 CPU 的消耗。

  2. Search 是无状态查询,Scroll 需要上下文维护:Scroll API 需要维护上下文信息,以便在后续的请求中能够正确地返回结果。这个上下文信息可能包括游标位置、排序信息、过滤条件等。为了保持这些上下文的一致性和完整性,ES 需要在服务器端维护和更新相关的状态。

  3. 这不意味着 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,从而造成稳定性问题。

优化方法:

  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 在实践能力上的更多可能性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1517203.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python Web开发记录 Day9:Django part3 用户管理

名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 1、数据库准备2、用户列表3、新建用户4、编辑用…

Linux之shell变量

华子目录 什么是变量?变量的名称示例 变量的类型变量的定义示例 自定义变量查看变量(自定义变量和全局变量) 环境变量定义环境变量(全局变量)法一法二法三env,printenv,export注意 C语言与shell…

计算机网络(001-1)

计算机网络-方老师 总时长 24:45:00 共50个视频,6个模块 此文章包含1.1到1.4的内容 简介 1.1计算机网络的作用 三网融合(三网合一) 模拟信号就是连续信号 数字信号是离散信号 1.2互联网概述 以前2兆带宽就要98 现在几百兆带宽也就几百块 …

Ajax学习笔记(一):原生AJAX、HTTP协议、AJAX案例准备工作、发送AJAX请求、AJAX 请求状态

目录 一、原生AJAX 1.1AJAX 简介 1.2 XML 简介 1.3 AJAX的特点 二、HTTP协议 三、AJAX案例准备工作 四、发送AJAX请求 1.发送GET请求 2.发送POST请求 3.JSON响应 IE缓存问题: 五、AJAX 请求状态 一、原生AJAX 1.1AJAX 简介 AJAX 全称为 Asynchronous …

Java基础-泛型

泛型 基本概念为什么我们需要泛型泛型类型泛型类简单泛型类多元泛型类 泛型接口泛型方法为什么要使用泛型方法呢?使用方法 泛型的上下限上限下限加点难度的例子例子一例子二例子三 深入理解泛型什么是泛型擦除后保留的原始类型泛型类型擦除原则如何进行擦除的?怎么证明存在类…

腾讯云轻量服务器地域选择教程以及不同地域的区别

腾讯云服务器地域怎么选择?不同地域之间有什么区别?腾讯云哪个地域好?地域选择遵循就近原则,访客距离地域越近网络延迟越低,速度越快。腾讯云百科txybk.com告诉大家关于地域的选择还有很多因素,地域节点选择…

WPF —— TabControl、StackPanel 控件详解

1 TabControl简介 表示包含多个项的控件,这些项共享屏幕上的同一空间。 TabControl有助于最大程度地减少屏幕空间使用量,同时允许应用程序公开大量数据。 TabControl包含共享同一屏幕空间的多个 TabItem 对象。一次只能看到 TabControl 中的一个 Ta…

Oracle登录错误ERROR: ORA-01031: insufficient privileges解决办法

这个问题困扰了我三个星期,我在网上找的解决办法: 1.控制面板->管理工具->计算机管理->系统工具->本地用户和组->ORA_DBA组。 但我电脑上根本找不到。 2.在oracle安装目录下找到oradba.exe运行。 最开始我都不到这个oradba.exe文件在哪…

数字生活的未来:探索Web3的全新世界

随着科技的飞速发展,我们正迈向一个数字化的未来。而在这个数字化的时代,Web3技术的崛起正引领着我们进入一个全新的世界。本文将深入探讨Web3技术的特点以及它给我们带来的全新体验。 1. 去中心化的特点 Web3的去中心化是其最显著的特点之一&#xff0…

设置jmeter默认语言为中文

问题描述 通过面板上面的选项修改语言(如下图),每次运行程序都需要重新再设置一遍,我需要每次打开都是中文界面 解决方案 进入jmeter的文件目录 bin——> jmeter.properties 打开这个文件 搜索Preferred GUI language在下方添…

linux环境基础开发工具1(vim 、 yum)

目录 前言 Linux编辑器-vim使用 Linux 软件包管理器 yum 前言 集成开发环境(IDE,Integrated Development Environment )是用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。集成了代码…

如何利用百度SEO优化技巧将排到首页

拥有一个成功的网站对于企业和个人来说是至关重要的,在当今数字化的时代。在互联网上获得高流量和优质的访问者可能并不是一件容易的事情,然而。一个成功的SEO战略可以帮助你实现这一目标。需要一些特定的技巧和策略、但要在百度搜索引擎中获得较高排名。…

爬虫3_爬取翻页URL不变的网站

之前实现了对大学排数据爬取:爬虫2_2019年549所中国大学排名. 近期复现代码,发现原网站升级,在翻页时,发现URL不改变,修改代码,使用网页自动化工具selenium实现对该类网站数据获取。 #-*- coding: UTF-8 -…

hadoop伪分布式环境搭建详解

(操作系统是centos7) 1.更改主机名,设置与ip 的映射关系 hostname //查看主机名 vim /etc/hostname //将里面的主机名更改为master vim /etc/hosts //将127.0.0.1后面的主机名更改为master,在后面加入一行IP地址与主机名之间的…

数字排列 - 华为OD统一考试(C卷)

OD统一考试(C卷) 分值: 200分 题解: Java / Python / C 题目描述 小明负责公司年会,想出一个趣味游戏: 屏幕给出 1−9 中任意 4 个不重复的数字,大家以最快时间给出这几个数字可拼成的数字从小到大排列位于第 n 位置…

稀碎从零算法笔记Day17-LeetCode:有效的括号

题型:栈 链接:20. 有效的括号 - 力扣(LeetCode) 来源:LeetCode 题目描述(红字为笔者添加) 给定一个只包括 (,),{,},[,] 的字符串 …

CentOS无法解析部分网站(域名)

我正在安装helm软件,参考官方文档,要求下载 get-helm-3 这个文件。 但是我执行该条命令后,报错 连接被拒绝: curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 # curl: (7) Fai…

Spring Boot 集成 WebSocket 实例 | 前端持续打印远程日志文件更新内容(模拟 tail 命令)

这个是我在 CSDN 的第一百篇原则博文,留念😎 #1 需求说明 先说下项目结构,后端基于 Spring Boot 3,前端为 node.js 开发的控制台程序。现在希望能够在前端模拟 tail 命令,持续输出后端的日志文件。 #2 技术方案 #2.…

手动创建线程池各个参数的意义?

今天我们学习线程池各个参数的含义,并重点掌握线程池中线程是在什么时机被创建和销毁的。 线程池的参数 首先,我们来看下线程池中各个参数的含义,如表所示线程池主要有 6 个参数,其中第 3 个参数由 keepAliveTime 时间单位组成。…

人工智能课题、模型源码

人工智能研究生毕业~深度学习、计算机视觉、时间序列预测(LSTM、GRU、informer系列)、python、人工智能项目代做和指导,各种opencv图像处理、图像分类模型(vgg、resnet、mobilenet、efficientnet等)、人脸检…