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

建设并维护一个亿级的搜索引擎并非易事,也不存在一劳永逸的最优治理方法。本文是在实践中不断学习和总结的成果,介绍了如何搭建一个可支持从千万级到亿级商品量级的搜索系统,并实现查询总 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.mzph.cn/news/709276.shtml

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

相关文章

androidapp的开发流程,王者笔记

昨天去面了一家公司,价值观有受到冲击。 面试官技术方面没的说,他可能是个完美主义的人,无论什么事情到了他那里好像都有解决的方案,我被说的无所适从,感觉他很厉害。 但我不能认可的是,面试官觉得加班是…

nextjs13如何进行服务端渲染?

目录 一、创建一个新项目 二、动态获取后端数据进行服务端渲染出现的问题 三、nextjs13如何进行服务端渲染 nextjs13是nextjs的一个重大升级,一些原本在next12当中使用的API在nextjs13上使用十分不便。本文将着重介绍在nextjs13及以上版本当中进行服务端渲染的方…

Linux - 基本指令

1、ls 指令 语法:ls [选项][目录或文件] 功能:对于目录,该命令列出该目录下的所有子目录与文件。对于文件,将列出文件名以及其他信息。 常用选项: -a 列出目录下的所有文件,包括以 . 开头的隐含文件-l …

Docker本地部署GPT聊天机器人并实现公网远程访问

文章目录 前言1. 拉取相关的Docker镜像2. 运行Ollama 镜像3. 运行Chatbot Ollama镜像4. 本地访问5. 群晖安装Cpolar6. 配置公网地址7. 公网访问8. 固定公网地址9. 结语 前言 随着ChatGPT 和open Sora 的热度剧增,大语言模型时代,开启了AI新篇章,大语言模型的应用非常广泛&…

Linux Shell脚本练习(一)

一、 Linux下执行Shell脚本的方式: 1、用shell程序执行脚本: a、根据你的shell脚本的类型,选择shell程序,常用的有sh,bash,tcsh等 b、程序的第一行#!/bin/bash里面指明了shell类型的,比如#!/…

Matlab: Introduction to Hybrid Beamforming

文章目录 来源混合波束赋形的基本概念System Setup关键函数 来源 在matlab的命令行输入 doc hybrid beamforming 混合波束赋形的基本概念 混合波束形成简介 本例介绍了混合波束形成的基本概念,并说明了如何模拟这种系统。 现代无线通信系统使用空间复用来提高散…

讲述微信小程序 sitemap.json 索引作用配置

做过pc端国内网址的朋友 对SEO这个词不会陌生 主要就是通过条件搜索网址 目前 我们小程序也有这样的功能提供 那么 因为我们百度seo优化的处理程度不同 被搜索出来的东西 会进行一个先后顺序的排序 那么 我们小程序的 sitemap.json 就是用来配置 我们小程序 是否允许被微信索…

mac终端操作

macOS ls 显示当前目录的所有文件夹 cd cd .. 进入jupyter notebook

java小记(1)

从java8开始,接口可以拥有默认的方法实现。 接口的成员(字段 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。 一个类可以实现多个接口,但不能继承多个抽象类。 重写:指子类实现了一个与父类在方法声明上…

物联网通信协议介绍

为了方便,将物联网通信协议分为两大类,一类是接入协议,一类是通讯协议。接入协议一般负责子网内设备间的组网及通信;通讯协议主要是运行在传统互联网TCP/IP协议之上的设备通讯协议,负责设备通过互联网进行数据交换及通…

web前端-html自定义列表

html 自定义列表 <!--有序列表 应用范围&#xff1a;试卷、问答--> <ol><li>Java</li><li>C</li><li>Python</li><li>C</li><li>VB</li> </ol><br><!--无序列表 应用范围&#xff1a…

3DGS进化,高效高质量的GaussianPro来袭!

作者&#xff1a;小张Tt | 来源&#xff1a;3DCV 在公众号「3DCV」后台&#xff0c;回复「原论文」获取论文 添加微信&#xff1a;dddvision&#xff0c;备注&#xff1a;3D高斯&#xff0c;拉你入群。文末附行业细分群 原文链接&#xff1a;3DGS进化&#xff0c;高效高质量…

大数据界面:客户又又又要求科技感了,如何破?

如果你问客户想要什么风格&#xff0c;大部分脱口而出科技感&#xff0c;不仅要求静态页&#xff0c;而且还要求动态效果&#xff0c;炫酷动画&#xff0c;贝格前端工场结合多个项目经历&#xff0c;帮助友友们梳理如何让界面科技动感。 一、没有科技感背后的潜台词 客户说大数…

[ffmpeg] x264 配置参数解析

背景 创建 x264 编码器后&#xff0c;其有一组默认的编码器配置参数&#xff0c;也可以根据需要修改参数&#xff0c;来满足编码要求。 具体参数 可修改的参数&#xff0c;比较多&#xff0c;这边只列举一些常用的。 获取可以配置的参数 方式1 查看 ffmpeg源码 libx264.c…

如何在宝塔面板中设置FTP文件传输服务并实现远程文件管理

文章目录 1. Linux安装Cpolar2. 创建FTP公网地址3. 宝塔FTP服务设置4. FTP服务远程连接小结 5. 固定FTP公网地址6. 固定FTP地址连接 宝塔FTP是宝塔面板中的一项功能&#xff0c;用于设置和管理FTP服务。通过宝塔FTP&#xff0c;用户可以创建FTP账号&#xff0c;配置FTP用户权限…

Leetcode—63. 不同路径 II【中等】

2024每日刷题&#xff08;115&#xff09; Leetcode—63. 不同路径 II 动态规划算法思想 实现代码 class Solution { public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int m obstacleGrid.size();int n obstacleGrid[0].size();…

Eureka 入门教程

Eureka 介绍 1. 注册中心概述 什么是注册中心&#xff1f; 给客户端提供可供调用的服务列表&#xff0c;客户端在进行远程调用&#xff08;RPC&#xff09;时&#xff0c;根据服务列表选择服务提供方的服务地址进行服务调用 注册中心的核心功能 注册&#xff1a;服务提供者上…

智能双星:遥测终端机与柳林“巡检机器人“,助力智能运维新升级!

随着科技的不断发展&#xff0c;智能化、自动化的运维管理已经成为企业追求高效、稳定运营的重要方向。柳林遥测终端机、柳林e拍云平台以及巡检机器人的组合&#xff0c;为企业带来了一种全新的、前置的、无感的智能运维体验。 柳林遥测终端机&#xff0c;以其强大的数据采集和…

java 大学生社团管理系统Myeclipse开发mysql数据库web结构jsp编程计算机网页项目

一、源码特点 java 大学生社团管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5…

unity使用Registry类将指定内容写入注册表

遇到一个新需求&#xff0c;在exe执行初期把指定内容写入注册表&#xff0c;Playerprefs固然可以写入&#xff0c;但是小白不知道怎么利用Playerprefs写入DWORD类型的数据&#xff0c;因此使用了Registry类 一. 对注册表中键的访问 注册表中共可分为五类 一般在操作时&#…