从 0 到 1 搭建亿级商品 ES 搜索引擎

news2024/11/17 19:32:08

建设并维护一个亿级的搜索引擎并非易事,也不存在一劳永逸的最优治理方法。本文是在实践中不断学习和总结的成果,介绍了如何搭建一个可支持从千万级到亿级商品量级的搜索系统,并实现查询总 QPS 从百级增长到千级,写入总 QPS 从百级增加到万级的过程。其中,ES 资源扩容是必不可少的,但除此之外,本文还将重点介绍一些扩容无法解决的 ES 性能问题。希望通过本文大家可以对 ES 的使用场景有更多数据和使用上的参考。由于篇幅有限,关于稳定性治理的部分将在下篇文章中进行介绍。

业务介绍

平台招商管理系统服务于抖音电商平台活动的多实体招商场景,会通过招商平台来进行收品,选品,然后分发品到各 C 端系统。招商的实体也非常的多样化,有达人直播间,商品招商,优惠券招商等等,其中商品招商是我们体量最大的招商实体。

招商平台服务架构

数据中心

数据中心是一个基于 ES 的搜索服务,提供可配置化的、可扩展的、通用的数据获取编排服务,是支持招商平台数据查询的通用服务。

关键概念了解:

  • 指标:指标是被我们用来描述一个实体或者对象的某个属性的元数据,比如商品名称,店铺体验分,达人等级,报名记录 ID,同时它也可以是某个对象的最小更新和获取单位,比如商品比价信息。一切有明确语义的字段我们都可以定义为指标

  • 集合:表示一组可通过某种共性收敛的集合,比如商品属性集合,店铺属性集合,分别可以用商品 ID,店铺 ID 去获取,也可以是商品报名记录集合,通过报名记录 ID 获取,它在业务上表达一组有关联关系的指标,和指标是1对多的关系。

  • Solution:数据获取方案,我们抽象出指标和集合两个概念,是为了数据可以以最小单位获取,并且可以不断横向扩展,Solution 帮我们抽象不同集合下的指标的获取方式。

  • 自定义表头:自定义表头即指任何一个二维行数据列表要展示的 Title,它和指标是 1 对多的关系;

  • 筛选项:筛选项即指任何一个二维行数据列表需要使用的筛选项,它可指标是 1 对 1 的关系;

  • 审核视图:审核视图指的是审核业务场景下,由一组自定义表头和一组筛选项可动态渲染出来的一个审核页面。

数据中心元信息设计

在功能设计中,通过指标-->【筛选项,自定义表头】-->审核视图-->最终动态渲染出一个审核页面的过程,由于我们是多实体多场景招商,不同实体不同场景需要不一样的审核视图,所以我们设计出来的这一系列能力,可以动态组合任何需要的审核视图效果。

数据中心就是为了上层业务提供通用数据获取能力的,包括数据同步,数据查询。数据来源目前有两个,外部 RPC 接口,以及报名记录 ES,数据中心整合了两套数据获取方案,对外完全无感知,即获取哪个集合下的哪些数据指标即可。

ES 搭建的意义就是为了支持招商报名记录的筛选统计能力的,为上层业务输出它想要的数据内容。

从 0 到 1 搭建 ES 集群

从 0 到 1 搭建系统,在满足基本业务需求的基础上,稳定性方面需要支持以下两点;

  • 基本容灾机制,是指当系统因为基础组件,以及读写流量变化使性能受到影响时,业务能及时自我调整。

  • 数据最终一致性,指报名记录 DB --> ES 多机房数据是完整的。

方案调研

ES 集群容量评估

ES 集群容量评估是为了保证集群搭建起来以后能够在未来一段时间内提供稳定服务的,主要需要能够解决以下问题:

  1. 每个索引应该设置多少分片,后续预估数据增量有多少,读写流量预估;

  2. 单个集群应该设置几个数据实例,单个数据实例采用什么规格;

  3. 了解垂直扩容和水平扩容的区别,当数据量超预期激增,或者流量超预期激增,我们的应对策略是什么,以及 ES 集群容灾应该怎样设计。

关键解决办法:

  1. ES 索引分片数一旦设定,不可修改,所以确定分片数很重要,通常分片数和 ES 实例成整数倍关系,保证负载均衡;

  2. 单个分片的大小在 10~30G 是比较合理的,索引过大会影响查询性能;

  3. 流量激增可以依靠扩容解决,数据激增可删除存量老旧数据或增加分片数解决;并且必须采用多机房容灾部署方案部署,互为容灾机房。

数据同步链路选型

主要解决 DB 报名记录如何同步到 ES,其它相关联的指标如何写入 ES,如何更新及保证数据的一致性。

  1. DB -> ES 需要是准实时数据流,报名记录等信息的变化必须是准实时可搜索到的;

  2. 报名记录除了本身字段还需要补充其报名商品,店铺,达人等属性字段,也写入到 ES,且能够支持部分更新,所以 ES 写入方式只能是 Upsert 方式;

  3. 单条报名记录更新必须是有序的,且不可冲突的。

ES 索引基本配置调研

了解必不可少的 ES 基本原理和配置。

  1. {"dynamic": false}避免 es mappings 自动膨胀,或新增非预期索引类型;

  2. index.translog.durability=async,异步刷新 translog 有利于提升写入性能,但是有丢数据风险;

  3. ES 默认 refresh interval为 1s,即表示数据写入成功后最快一秒可以查到。

数据同步方案

数据同步链路图

DB --> ES 数据同步方案,最终采用的是异构数据同步写 RocketMQ + Flink 多机房消费的方式,同时当报名记录首次写入时通过 Faas 自定义转换脚本填充扩展指标,扩展指标的更新依赖变更消息监听和定时任务两种方式。调研的时候,DB -> ES 多机房的方案其实有三种,最终我们选择了第三种方案,以下我们对比下三个方案的差异点:

方案一:通过 异构数据同步(Dsyncer)直接写入 ES 多机房

缺点:

  1. 直写在满足 ES 同步部署多机房的诉求上是处于弱势的,因为无法保证多机房同时写成功,那部署多个异构数据分别写可以吗?可以的,即工作量增加三倍,大约十几个索引。

  2. 直写 Bulk 的写入能力是相对弱的,随着流量波动写入尖刺也会比较明显,对 ES 的写入性能不友好。

  3. 直写无法保证 ES 多更新入口的情况下单条报名记录的有序更新,增加全局 Version 版本可以?可以的,但太重了。

优点:依赖路径最短,写入延迟低,系统风险最小,对于小流量的业务,以及同步场景简单的业务是完全没问题的。

方案二:通过 RocketMQ 写 ES 单机房

在 DB 通过 RocketMQ 写 ES 单机房后,通过 ES 提供的数据跨集群复制能力,将数据同步到其它机房。

方案三:通过 RocketMQ + Flink 方式写 ES 多机房

在 DB 通过 RocketMQ 写 ES 集群时,分别起多个独立的 Conusmer Group 任务,系统可采用 Flink 分布式系统,将数据分别写入多个机房。

方案二和方案三的区别点只有一个:就是写多机房的方式不同,方案二是写到一个机房,然后将数据准实时同步到其它机房,而方案三是其多个独立的 Consumer 分别写多机房。

方案二和方案三的缺点是一样的:依赖路径最长,写入延迟容易受基础组件抖动的影响,然而方案二的致命缺点是系统存在单点风险,假设通过 LF 同步数据到 HL 和 LQ,那么在 LF 挂掉之后系统也就无法使用了。

方案三的优点是多机房写入链路互相独立,相比方案二任何一条链路出问题,都不会对业务造成风险;RocketMQ 能轻松解决单 Key 顺序更新问题,这也是方案一不可取的原因

为什么通过 RocketMQ 写入就能解决乱序和冲突问题呢?

  1. 首先 ES 写入是基于 Version 版本号做乐观锁控制的,如果同时并发更新同一条记录,那么我们同时拿到的 Version 版本是一样的,假设是1,那么大家都将 Version 更新成2去写入,就会发生冲突,总是发生冲突就会造成丢失更新的问题;

  2. 一般业务场景都是需要保证基于 Key 有序消费,也是 Partition 有序消费,有序消费需要有两个必要条件:消息被存储时保持和发送的顺序一致;消息被消费时保持和存储的顺序一致。

所以业务想要消息的有序消费,就需要保证发送消息同 Key 发送到同一个 Partition,消费消息保证同 Key 消息始终被同一个 Consumer 消费。但事实上,上面提到的两个必要条件是理想状态下的,有些情况下是没法完全保证的,比如 Consumer Rebalance,比如写某 Broker 实例一直失败,具体下面会再分析出现原因和解决版本。


一张图说明 RocketMQ 分区有序

  • 对于指定的一个 Topic,所有消息根据 Sharding Key 分成多个(Queue)。

  • 同一个 Queue 内的消息按照严格的 FIFO 顺序进行发布和消费。

  • Sharding Key 是顺序消息中用来区分不同分区的关键字段,和普通消息的 Key 是完全不同的概念。

  • 适用场景:性能要求高,根据消息中的 Sharding Key 去决定消息发送到哪一个 Queue,一般分区有序就可以满足我们的业务要求,同时性能高。

这里需要注意的是通过 RocketMQ 也许已经帮业务解决 99% 的乱序问题了,但并不是 100%,极端情况下消息仍可能出现乱序消费问题,比如发生 ABA 现象,比如 Partiton 故障时消息被重复发送到其他 Partition 队列等,所以一致性对账必不可少。

多层对账机制

对账机制是解决 DB->ES 的数据一致性问题的,前面说 DB --> ES 是准实时数据流,并且依赖链路比较长,它在不同的状态下,我们都需要有对应的监控,对账和补偿策略,保证数据最终一致性。

这里我们是做了三层对账,通过对账平台对账,实现分钟级对账,以及离线对账,需要多层对账的原因会在下文一一进行解释。

DB 同步 ES 链路故障分析图

业务校验平台(BCP)秒级对账

参考上图,会发现 DB --> ES 同步依赖依赖组件比较多,这种情况下我们更需要一个全局视角的对账来发现同步链路问题,即 BCP 实时对账。

BCP 对账是监听 Binlog 直接查 ES 多机房对账的单流对账,仅依赖 Binlog 流,中间环节出现的数据同步延迟,或者阻塞都可通过 BCP 对账快速发现;细心的同学会发现,如果 Binlog 断流,BCP 对账就对不出来了,后面说怎么解决这种情况,但至少可以看出来, 除了 DB->DBus,BCP 对账足以发现大部分同步延迟问题。为什么采用单流而非多流?

  1. 避免多流对账的数据流链路较长,会带来的不可控延迟问题,导致校验准确性偏低。

  2. BCP 对账的维护成本会大大降低,因为采用多流的话,多机房对账我们需要维护多份 BCP 对账,这其中依赖维护的基础组件更多。

分钟级对账

上节说到业务校验平台(BCP)对账覆盖不到的路径是 DB->DBus,也就是 Binlog 断流的情况。通常 Binlog 断流可能已经意味着更严重的事故,但我们要做到的就是方方面面。

分钟级别对账是直接查询 DB 和 ES 进行对账,不依赖任何组件,当发生不一致时则自动补偿。分钟级别对账一方面弥补 BCP 对账的不足,第二点则是加入了补偿机制。BCP 不补偿的原因是因为 BCP 主要还是为了发现问题,所以要保持轻量快速,还有就是它依然依赖 RocketMQ,DBus 等基础组件,这种补偿仍然覆盖不住所有异常场景。

三分钟一次的对账默认情况下我们会认为组件功能完好,只是某节点出现短暂延迟而产生补偿,如果频繁发生补偿报警就需要进一步分析链路到底是哪里出了问题?此时在我们的场景下我会把链路一分为二,确认下 RocketMQ 之前链路出问题了,还是 RocketMQ 以及后续消费链路出现的问题。通过故障分析图,如果是 RocketMQ 之前链路出问题,比如 Binlog 断流、异构数据同步平台组件挂了等,则补偿数据直接写到 RocketMQ 中,消费到多机房的,此时读流量不用切流,且能够保证多机房数据的一致性。但如果 RocketMQ 挂了就会直接去写 ES 了,因为此时我们无法保证多机房同时写成功,所以我们的决策是只写单机房,将所有流量切换到单机房。

RocketMQ挂掉是个非常不好的信号,这里情况是比较复杂的。因为直写 ES,如果写流量高,系统此时失去了限流保护,ES 不一定扛得住;单机房不一定能够同时承受所有读流量;如果频繁发生写入冲突还需要做业务写入口降级。所以 RocketMQ 挂掉,可以理解为写链路的中枢系统瘫痪了,这是最不想看到的情况,所以 RocketMQ 的 SLA 是业务的基线。

T+1 离线对账

离线对账,是将 DB 和 ES 的数据天级同步到 Hive,增量数据校验最终一致性,如果不一致则自动发起补偿,离线对账是同步链路数据一致性的底线,数据最迟 T+1 补偿成功。

总结

以上我们已经完成了第一阶段的搭建,完成了容灾部署,一致性对账,以及基本系统异常应对策略。此时 ES 可以支持千万级别的商品索引的读写请求,单机房流量在 500 ~ 100 QPS 之间波动,写流量基本维持在 500 QPS 左右。

但随着业务的发展,ES 集群多次出现 CPU 暴涨,一个或多个机房同时被打满,查询延迟突然增加,然而读写流量却波动不大,或远不及系统峰值的情形,这种风险归源于 ES 集群出现的性能问题,以及业务的使用姿势问题。这部分内容我们将在下篇 ES 搜索引擎的稳定性治理中为大家继续介绍。

文章来源|字节跳动商业平台 王丹

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

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

相关文章

数据卷(Data Volumes) 自定义镜像(dockerfile)

目录 一. 数据卷(Data Volumes) 1.1 什么是数据卷 1.2 为什么需要数据卷 1.3 数据卷的作用 1.4 数据卷的使用 二. 自定义镜像(dockerfile) 2.1 什么是dockerfile 2.2 自定义centos 2.3 自定义tomcat 一. 数据卷(Data…

【学习记录】HC32F460USB——U盘IAP升级app

从头开始,万物从解压开始 直奔猪蹄,找到usb下的工程文件 开始移植 主要移植IAP的boot和fatfs的文件系统,fatfs官网去下载ff15.0版本,目前用这个 放到项目里 添加到工程文件中 改引脚,给USB放电 编译,可以…

java自动化之自动化框架项目(第三天-测试数据注入到测试方法)

接第二天 1.实现目标 这里我们是数据驱动方式,把数据注入到测试方法,在测试方法中就可以获取对象中的数据。 2.注入测试数据 上一篇我们已经把用例数据封装到对象并放到list中,这里我们把用例对象list中的对象分别放到Object类型的一维数…

element-upload 文件上传和图片上传

文件上传 element-upload介绍实际上的文件上传代码前端java后端 补充 element-upload介绍 element-ui是一个很常用的文件上传组件,他包括但不局限于只上传图片,很多时候用于系统的头像修改就是用这个组件 在官网element-ui(element-upload&a…

区块链游戏解说:什么是 Arcade Champion

作者:lesleyfootprint.network 编译:cicifootprint.network 数据源:Arcade Champion Dashboard 什么是 Arcade Champion Arcade Champion 代表了移动游戏世界的重大革新。它将经典街机游戏的怀旧与创新元素结合在一起,包括 NF…

《最新出炉》系列初窥篇-Python+Playwright自动化测试-33-处理https 安全问题或者非信任站点-上篇

1.简介 这一篇宏哥主要介绍playwright如何在IE、Chrome和Firefox三个浏览器上处理不信任证书的情况,我们知道,有些网站打开是弹窗,SSL证书不可信任,但是你可以点击高级选项,继续打开不安全的链接。举例来说&#xff0c…

redis中的分布式锁(setIfAbsent)(expire)

目录 应用场景 代码实例1: 代码实例2: setIfAbsent: expire: 举例说明: 代码实例3: 代码实例4: 还是一个同事问的一个问题,然后闲着没事就记录下来了。多人操作同一个保单&a…

(Linux学习二)文件管理基础操作命令笔记

Linux目录结构: bin 二进制文件 boot 启动目录 home 普通用户 root 超管 tmp 临时文件 run 临时运行数据 var 日志 usr 应用程序、文件 etc 配置文件 dev 文件系统 一、基础操作 在 Linux 终端中,你可以使用以下命令来清屏: clear 命令&am…

ubuntu2204部署hbase2.3.7

开启root 修改root用户的密码 sudo passwd rootSSH放行 sudo sed -i s/^#\?PermitRootLogin.*/PermitRootLogin yes/g /etc/ssh/sshd_config; sudo sed -i s/^#\?PasswordAuthentication.*/PasswordAuthentication yes/g /etc/ssh/sshd_config;重启服务 sudo service ssh…

IDEA利用鼠标调整字体大小

就可以按住ctrl和鼠标调节代码字体的大小啦! 如果有用,记得给我来个赞~ 谢啦!

什么是生成式人工智能?

近年来,人工智能取得了重大进展,其中发展迅速的领域之一就是生成式人工智能。生成式人工智能是人工智能和深度学习的一个子领域,主要使用机器学习技 术根据现有数据训练算法和模型,生成诸如图像、文本、音乐、视频等新内容。 要更…

nginx 反向代理 与缓存功能

一 理论说明 (一)反向代理简介 反向代理:reverse proxy,指的是代理外网用户的请求到内部的指定的服务器,并将数据返回给用户的一种方式,这是用的比较多的一种方式。 即 代理服务机 Nginx 除了可以在企…

linux系统-----------搭建LNMP 架构

PHP(Hypertext Preprocessor 超文本预处理器)是通用服务器端脚本编程语言,主要用于web开发实现动态web页面,也是最早实现将脚本嵌入HTML源码文档中的服务器端脚本语言之一。同时,php还提供了一个命令行接口,因此,其也可…

如何使用便签快速分类工作待办事项

在日常工作和生活中,我们经常需要处理各种各样的待办事项。而有效地分类这些任务,可以帮助我们更好地管理时间和提高工作效率。使用便签是一种简单而实用的方法,下面将介绍如何利用好用便签来快速分类工作待办事项。 首先,你可以…

软考中级1(数据库系统工程师)

1.程序计数器 保存待读取指令的地址 累加器 保存原操作数和结果 2.DMA方式不需要CPU,由DMA控制器直接控制数据的传送 3.数据位n位,校验位k位,海明码满足的关系:2^k-1>nk 4.高速缓存Cache:位于CPU和主…

大数据分布式计算工具Spark实战讲解

PySpark 什么是PySpark? Spark是Apache基金会旗下的顶级开源项目,用于对海量数据进行大规模分布式计算。 PySpark是Spark的Python实现,是Spark为Python开发者提供的编程入口,用于以Python代码完成Spark任务的开发 PySpark不仅可…

三天学会阿里分布式事务框架Seata-SpringCloud Alibaba分布式基础案例搭建

锋哥原创的分布式事务框架Seata视频教程: 实战阿里分布式事务框架Seata视频教程(无废话,通俗易懂版)_哔哩哔哩_bilibili实战阿里分布式事务框架Seata视频教程(无废话,通俗易懂版)共计10条视频&…

记录工作中遇见问题、学习项

1、判空操作 Demo demo Optional .ofNullable(demoService.getById(id)) .orElseThrow(() -> new ServiceException("不存在id为" id "的数据")); 2、SQL方面 1、group by : GROUP BY 子句必须放在 WHERE 子句中的条件之后&#…

网络:IPv6

1、由于IPv4地址资源枯竭,所以产生了IPV6。 版本长度地址数量IPv432 bit4 294 967 296IPv6128 bit340 282 366 920 938 463 374 607 431 768 211 456 2、IPv6的基本报头在IPv4报头基础上,增加了流标签域,去除了一些冗余字段,使报…

Apache SeaTunnel 及 Web 功能部署指南(小白版)

在大数据处理领域,Apache SeaTunnel 已成为一款备受青睐的开源数据集成平台,它不仅可以基于Apache Spark和Flink,而且还有社区单独开发专属数据集成的Zeta引擎,提供了强大的数据处理能力。随着SeaTunnel Web的推出,用户…