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

继上文在完成了第一阶段 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.rhkb.cn/news/276119.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2.MongoDB与关系数据库对比

MongoDB的简单操作与比较 与关系数据库对比 MySQL与MongoDB都是开源的常用数据库,但是MySQL是传统的关系型数据库,MongoDB则是非关系型数据库,也叫文档型数据库,是一种NoSQL的数据库。它们各有各的优点,来看看他们之…

AttributeError: cannot assign module before Module.__init__() call

原因 调用了自定义的类,但是在自定义的类的__init__函数下面没有写super( XXX, self ).init() 错误案例 import torch import torch.nn as nnclass SelfAttention(nn.Module):""" Self-Attention """def __init__(self, n_head, d…

海外媒体宣发套餐推广:如何选择最佳方案-华媒舍

在信息时代,传播和宣传已经成为各个行业发展的关键部分。尤其对于拓展国际市场的企业来说,海外媒体宣发更是至关重要。由于各种原因,很多企业在选择海外媒体宣发套餐时感到困惑。本文将为您介绍如何选择最佳的海外媒体宣发方案。 1.了解目标市…

mysql数据库备份学习笔记

数据库备份 方法1 物理备份:xtrabackup 方法2 逻辑备份 mysqldump 参考备份与恢复的方法: 【MySql】Mysql之备份与恢复_mysql数据库备份与还原-CSDN博客 可以借鉴的物理备份: 思路是 先做一次全量备份,然后每天做一次增量备份…

pta—剪切粘贴

使用计算机进行文本编辑时常见的功能是剪切功能(快捷键:Ctrl X)。请实现一个简单的具有剪切和粘贴功能的文本编辑工具。 工具需要完成一系列剪切后粘贴的操作,每次操作分为两步: 剪切:给定需操作的起始位置…

1.2 课程架构介绍:STM32H5 芯片生命周期管理与安全调试

1.2 课程架构介绍:STM32H5 芯片生命周期管理与安全调试 下面开始学习课程的第二节,简单介绍下STM32H5芯片的生命周期和安全调试,具体课程大家可以观看STM32官方录制的课程,链接:1.2. 课程架构介绍:STM32H5…

【爬虫开发】爬虫从0到1全知识md笔记第1篇:爬虫概述【附代码文档】

爬虫开发从0到1全知识教程完整教程(附代码资料)主要内容讲述:爬虫概述。selenium的其它使用方法。Selenium课程概要。常见的反爬手段和解决思路。验证码处理。chrome浏览器使用方法介绍。JS的解析。Mongodb的介绍和安装,小结。mongodb的简单使…

一、C#冒泡排序算法

一、C#冒泡排序算法 简介 冒泡排序算法是一种基础的排序算法,它的实现原理比较简单。核心思想是通过相邻元素的比较和交换来将最大(或最小)的元素逐步"冒泡"到数列的末尾。 实现原理 冒泡排序是一种简单的排序算法,其…

《系统架构设计师教程(第2版)》第6章-数据库设计基础知识-04-应用程序与数据库的交互

文章目录 1. 库函数级别访问接口2. 嵌入SQL访问接口2.1 概述2.2 数据库厂商 3. 通用数据接口标准3.1 开放数据库连接 (ODBC)3.2 一些数据库接口1)数据库访问对象 (DAO)2)远程数据库对象 (RDO)3)ActiveX数据对象 (ADO)4)Java数据库…

python爬虫实战——小红书

目录 1、博主页面分析 2、在控制台预先获取所有作品页的URL 3、在 Python 中读入该文件并做准备工作 4、处理图文类型作品 5、处理视频类型作品 6、异常访问而被中断的现象 7、完整参考代码 任务:在 win 环境下,利用 Python、webdriver、JavaS…

在Django中使用PyJWT实现登录及验证功能

目录 1、安装PyJWT 2、对信息加密及解密 3、配置登录视图和及url 4、登录装饰器 5、在验证有登录权限的的视图中登录 PyJWT的使用 1、安装PyJWT pip isntall pyjwt 2、对信息加密及解密 import jwt import datetime from jwt import exceptions# 加密盐 JWT_SALT &qu…

Spring Boot中Excel数据导入导出的高效实现

🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 &#x…

《Ubuntu20.04环境下的ROS进阶学习4》

一、发布地图消息 在上一小节中我们已经获取到了激光雷达的数据,在本接中我们将学习地图数据格式和如何发布地图数据。 二、了解地图数据格式 首先登录 index.ros.org 网站搜索一下map_server消息包。您也可以直接搜这个网址 map_server - ROS Wiki ,下翻…

QT 如何防止 QTextEdit 自动滚动到最下方

在往QTextEdit里面append字符串时,如果超出其高度,默认会自动滚动到QTextEdit最下方。但是有些场景可能想从文本最开始的地方展示,那么就需要禁止自动滚动。 我们可以在append之后,添加如下代码: //设置编辑框的光标位…

水库大坝安全监测中需要注意的事项

随着经济和社会的发展,水资源的需求也在不断增加。因此,建设水库已成为保障水资源的主要方式之一。然而,随着水库规模的增大和工程的复杂性的增加,水库大坝的安全问题也日益引起重视。为此,需要对水库大坝进行安全监测…

链路聚合实验(华为)

思科设备参考:链路聚合实验(思科) 一,技术简介 网络设备的链路聚合技术(Link Aggregation)是一种将多个物理链路捆绑在一起,形成一个逻辑链路的技术。这样做可以增加带宽、提高可靠性和实现负…

Spring Cloud Alibaba微服务从入门到进阶(四)(服务发现-Nacos )

Nacos是服务发现组件和配置服务器 作为服务发现注册组件Nacos Server 搭建Nacos Server 下载、启动 访问nacos,默认 用户/密码 nacos/nacos 将应用注册到nacos 加依赖 在SpringCloud子项目中,Feign是属于OpenFeign,Sentinel、Nacos是属于alibaba的&…

Python数据分析-4

1.对于一组电影数据,呈现出rating,runtime的分布情况: #encodingutf-8 import pandas as pd import numpy as np from matplotlib import pyplot as plt file_path "./youtube_video_data/IMDB-Movie-Data.csv" df pd.read_csv(file_path) …

新火种AI|GPT-4诞生1年,OpenAI把它放到了机器人上

作者:一号 编辑:美美 ChatGPT拥有了身体,机器人也有了灵魂。 从OpenAI在去年3月14日拿出GPT-4后,已经过了整整一年。显然,在GPT-4诞生之后的这一年,一切都迭代得太快了,从GPT-4展现多模态能力&…

【源码独家】GPU池化平台 AI训练平台 AI推理平台

GPU池化软件 | (AI人工智能训练平台、AI人工智能推理平台) 讨论群v:🚀18601938676 一、AI人工智能开发-------------面临的问题和挑战 1. GPU管理难题 1.1 资源管理难:算力资源昂贵,但是缺乏有效管理,闲置情况严重。 1.2 用户…