滴滴 Redis 异地多活的演进历程

news2024/10/7 4:36:52

为了更好的做好容灾保障,使业务能够应对机房级别的故障,滴滴的存储服务都在多机房进行部署。本文简要分析了 Redis 实现异地多活的几种思路,以及滴滴 Redis 异地多活架构演进过程中遇到的主要问题和解决方法,抛砖引玉,给小伙伴们一些参考。

Redis 异地多活的主要思路

业界实现 Redis 异地多活通常三种思路:主从架构、Proxy双写架构、数据层双向同步架构。

主从架构

dc32c83646230825ff660d459951c9e3.png

主从架构的思路:

  1. 各机房的 Redis 通过 Proxy 对外提供读写服务,业务流量读写本机房的 Redis-proxy

  2. 主机房里的 Redis-master 实例承担所有机房的写流量

  3. 从机房里的 Redis-slave 实例只读,承担本机房里的读流量

主从架构的优点

  • 实现简单,在 Proxy 层开发读写分流功能就可以实现

  • Redis 层使用原生主从复制,可以保证数据一致性

主从架构的缺点

  • 从机房里的 Redis-proxy 需要跨机房写,受网络延时影响,业务在从机房里的写耗时高于主机房

  • 主机房故障时,从机房的写流量也会失败,需要把从机房切换为主机房,切换 Redis-master

  • 网络故障时,从机房的写流量会全部失败,为了保障数据一致性,这种场景比较难处理

Proxy 双写架构  

b56b4351ace4eccc894a167f2af12769.png

Proxy 双写架构的思路:

  1. 各机房的 Redis 通过 Proxy 对外提供读写服务,业务流量读写本机房的 Redis-proxy

  2. 不区分主从机房,每个机房都是独立的 Redis 集群

  3. 各机房的读写流量都是访问本机房的 Redis 集群

  4. Proxy 层在写本机房成功后,将写请求异步发送到对端机房

Proxy 双写架构的优点:

  • 实现简单,在 Proxy 层开发双写功能就可以实现

  • 一个机房故障时,其他机房的流量不受影响

  • 网络故障时,各机房内部的流量也不受影响

Proxy 双写架构的缺点:

  • 不能保证数据一致性,Proxy 异步 write 请求可能会失败,失败丢弃请求后,导致双机房数据不一致

  • 假设机房-A的集群先上线,机房-B 后上线,Proxy 双写架构不能支持把机房-A的存量数据同步到机房-B

  • 网络故障时,异步 write 会失败后丢弃,网络恢复后,之前失败的数据已经丢弃,导致双机房数据不一致

数据层双向同步架构   

60b713379f14423a84cc02b73d5a5fd7.png

数据层双向同步架构的思路:

  1. Proxy 不关心底层 Redis 数据同步

  2. 业务流量只访问本机房里的 Redis 集群

  3. 在 RedisServer 层面实现数据同步

数据层双向同步架构的优点:

  • 机房-A故障时,机房-B不受影响,反向如是

  • 网络故障时,本机房流量不受影响,网络恢复后,数据层面可以拉取增量数据继续同步,数据不丢

  • 支持存量数据的同步

  • 业务访问 Redis 延时低,访问链路不受机房间网络延时影响

  • 业务单元化部署时,双机房 Redis 会有较高的数据一致性

数据层双向同步架构的缺点:

  • 实现相对比较复杂,RedisServer 改动比较大

滴滴 Redis 架构

Codis 架构(早期架构,现已废弃)

df6f8363ef0b1b99aeb301ab441b0d4d.png

Kedis 架构(线上架构)

47a243d5d33a003b1cca272b608eecea.png

滴滴 Redis 异地多活架构的演进

第一代多活架构       

de33ee962fb974d03d43434b780a65ad.png

第一代 Redis 多活基于 Codis 架构在 proxy 层实现了双写,即本机房的 Proxy 将写流量转发到对端机房的 Proxy,这个方案的特点是快速实现,尽快满足了业务多机房同步的需求。如前面 Proxy 双向架构思路所讲,本方案还存在着诸多缺点,最主要的是网络故障时,同步数据丢失的问题,为了解决这些问题,我们开发了第二代多活架构。

第二代多活架构

39596cde2376c39afa83f322cde0724a.png

130e3adce62dfde1bc1a997d1f2d44e1.png

第二代多活基于 Kedis 架构,对 Redis-server 进行改造,可以把增量数据从 Redis 直接写入本机房的 MQ 中,由对端机房的 consumer 来消费 MQ,consumer 将数据写入对端 Redis 中。网络故障时,数据会在 MQ 堆积,待网络恢复后,consumer 可以基于故障前的 offset 继续进行消费,写入对端 Redis,从而保证在网络故障时 Redis 多活不会丢数据。

但这一代架构仍不够完美,存在以下问题:

  • ProducerThread 把数据写入 MQ 时,如果触发 MQ 限流,数据会被丢掉

  • RedisServer 内部包含了 ProducerThread,当中间内部 queue 累积数据量超过10000条时,数据会被 MainThread 丢掉

  • 中间同步数据写入 MQ,增加了跨部门依赖,同步链路长,不利于系统稳定性

  • 中间同步链路重试会造成非幂等命令执行多次,例如 incrby 重试可能造成命令执行多次造成数据不一致

  • 对于新建双活链路,不支持同步存量数据,只能从当前增量数据开始同步

  • Redis 增量数据写入 MQ,导致成本增加

为了解决以上问题,我们开发了第三代架构。

第三代多活架构

在第三代架构中,我们细化了设计目标,主要思路是保证同步链路中的数据不丢不重,同时去掉对 MQ 的依赖,降低多活成本。

c21863acc90499f36b7c2c6f55fa5fbe.png

第三代架构中,我们去掉了 MQ 和 consumer,新增了 syncer 组件。syncer 组件模拟 Redis-slave 从 Redis-master 中拉取增量数据,这样把数据同步和 Redis 进行解耦,便于后续多机房扩展。

在第三代架构中,Redis 遇到了回环、重试、数据冲突、增量数据存储和读取等问题,接下来一一介绍我们应对这些问题的解决方案。

1、回环问题

机房-A 写入的数据同步到机房-B,防止数据再传回机房-A。

01502b752c4ee628d3131d92b91bd389.png

为了解决回环问题,我们开发了防回环机制:

  1. Redis 增加 shardID 配置,标识唯一分片号

  2. Redis 请求中增加 opinfo,记录元信息,包含 shardID   

330b438fb756ea76d700c63be105d274.png

  • 机房-A 的 Proxy 写入了 set k v 请求

  • 机房-A 的 Redis-master 向 syncer 同步 set k v opinfo[shardID-1] 请求

  • syncer 向机房-B 写入 set k v opinfo[shardID-1] 请求

  • 这样机房-B 根据 shardID-1 识别出这条请求是机房-A 生产的数据,因此不会再向机房-A 同步本条请求

2、重试问题

机房-A 写入的 incrby 请求同步到机房-B,由于中间链路的重试,导致机房-B 可能执行了多次。 

60c3c3bf175fab532be2fa58b2fe9dcc.png

为了解决重试问题,我们开发了防重放机制:

  1. Redis 增加 opid,标识唯一请求号

  2. Redis 请求中增加 opinfo,记录元信息[opid]

523135f8310d9e434c494162a715e5b2.png

  • 机房-A 的 Proxy 写入了 incrby k 1 请求

  • 机房-A 的 Redis-master 向 syncer 同步了 incrby k 1 opinfo[opid=100] 请求, 之前同步的 opid=99 的请求已经成功

  • syncer 向机房-B 写入 incrby k 1 opinfo[opid=100] 请求

  • 机房-B 的 Redis 里存储了防重放信息 shardID-1->opid[99]

  • 机房-B 的 Redis 发现新请求的 opid=100>本地的99,判断为新请求

  • 机房-B的 Redis 执行这条请求,并把防重放信息更新为shardID-1->opid[100]

  • 假设机房-A 的 syncer 将本条请求进行了重试,又执行了一遍 incrby k 1 opinfo[opid=100]

  • 机房-B 的 Redis 发现新请求 opid=100 等于本地的100,判断为重复请求

  • 机房-B 的 Redis 忽略掉本地请求,不执行

3、数据冲突问题

双机房同时修改同一个 key 导致数据不一致

1b00ab699a3dc59bbab23eda14a72041.png

对于数据冲突,不同数据类型的不同操作的数据合并,如果单从存储层解决,是一个非常复杂的话题。如果业务层做了单元化部署,则不会出现这种问题。如果业务层没有做单元化,我们开发了冲突检测功能,来帮助业务及时发现数据冲突,最后数据以哪边为准来修正,需要业务同学来决策。

冲突检测机制:

  1. Redis 记录 key 的最后 write 时间

  2. Redis 请求中增加 opinfo,记录元信息 [timestamp]

  3. 如果 opinfo.timestamp<=key_write_time,则记录冲突 key

f6116f6bc59989b9aa45880b0b6dd090.png

时间T1<T2<T3

  • T1时间,用户在机房-A 写入请求 set k v1

  • T2时间,用户在机房-B 写入请求 set k v2,并记录k的最后修改时间为T2

  • 由于网络同步延时,T3时间,syncer 把T1时间写入的 set k v1请求发送到了机房-B

  • 机房-B 的 Redis 执行 set k v1 时发现 timestamp 为T1,但 k 的最后修改时间为T2

  • 由于T1<T2,机房-B 的 Redis 判断这是一次冲突,并记录下来,然后执行该条请求

以上是冲突检测的基本原理,这是一个旁路统计,帮助用户发现一些潜在冲突数据。

4、增量数据存储和读取问题

因为 syncer 只是同步组件,不会存储数据,所以需要考虑当网络故障时,增量数据的存储和读取问题。

7a10335c5ae8fa90d640da1c6fb9785d.png

为了解决这个问题,我们对 Redis 的 aof 机制进行了改造,可以在网络故障时,增量数据都堆积在 Redis 的磁盘上,在网络恢复后,syncer 从 Redis 里拉取增量 aof 数据发送到对端机房,避免数据丢失。

aof 机制改造有:aof 文件切分、aof 增量复制、aof 异步写盘

34fb53592553e604e57d02c4e1c85529.png

  • 将 aof 文件切分为多个小文件,保存增量数据

  • 当增量数据超过配置的阈值时,Redis 自动删除最旧的 aof 文件

  • 当 Redis 重启时,加载 rdb 文件和 rdb 之后的 aof 文件,可以恢复全部数据

  • 当网络故障恢复后,syncer 根据故障前的 opid 向 Redis 请求拉取增量数据,发送到对端机房

feaef328382a14af99b046f6f0ec28cd.png

开源 Redis 是在主线程中进行 aof 写盘,当磁盘 IO 过高时,Redis 写盘可能造成业务访问 Redis 耗时抖动。因此我们开发了 aof 异步写盘机制:

  • Redis 的主线程将 aof 数据写入 queue 中

  • bio 线程来消费 queue

  • bio 线程将 aof 数据写入磁盘

这样 Redis 的访问耗时不受磁盘 IO 的影响,更好的保证稳定性。

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

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

相关文章

Unity Meta Quest 一体机开发(六):HandGrabInteractor 和 HandGrabInteractable 知识点

文章目录 &#x1f4d5;教程说明&#x1f4d5;HandGrabInteractor⭐HandGrabAPI⭐HandWristPoint⭐GripPoint⭐PinchPoint⭐PinchArea⭐HandGrabVisual⭐HandGrabGlow &#x1f4d5;HandGrabInteractable⭐Support Grab Type⭐Pinch Grab Rules 和 Palm Grab Rules⭐Unselect M…

【SpringBoot3+Vue3】三【实战篇】-后端(优化)

目录 一、登录优化-redis 1、SpringBoot集成redis 1.1 pom 1.2 yml 1.3 测试程序&#xff08;非必须&#xff09; 1.4 启动redis&#xff0c;执行测试程序 2、令牌主动失效&#xff08;代码优化&#xff09; 2.1 UserController设置token到redis 2.2 登录拦截器Log…

下载huggingface预训练模型到本地并调用

写在前面 在大模型横行的时代&#xff0c;无法在服务器上连接外网的研究僧真的是太苦逼了&#xff0c;每次想尝试类似于CLIP&#xff0c;BLIP之类的大模型都会得到“requests.exceptions.ConnectionError: (MaxRetryError("HTTPSConnectionPool(host‘huggingface.co’, …

Win11系统安装或执行程序时提示:文件系统错误(-1073740771)解决方案

有用户反映&#xff0c;exe文件无法执行或者无法安装&#xff0c;报错如图所示&#xff1a; 解决方法&#xff1a; 方法一&#xff1a; 1.打开控制面板&#xff0c;可以采用”搜索“→”控制面板“的方式 2.控制面板选择“用户账户”&#xff0c;再选择“更改用户账户控制设…

TiDB单机集群模拟生产环境

1、先部署环境&#xff0c;安装5.4.3版本&#xff0c;详细的安装步骤见官方文档&#xff1a;单机集群模拟生产环境安装教程 配置文件topo.yaml global:user: "tidb"ssh_port: 22deploy_dir: "/tidb-deploy"data_dir: "/tidb-data"monitored:no…

【数据结构】线段树(点修区查)

数据结构-线段树&#xff08;点修区查&#xff09; 前置知识 分治递归二叉树 思路 我们需要维护一个支持单点修改&#xff0c;区间查询的数据结构&#xff0c;并且要求在线&#xff0c;一般使用线段树解决。 线段树是一个二叉树形的数据结构。 线段树的思想很简单&#xff0c…

Python---数据序列中的公共方法

公共方法就是 支持大部分 数据 序列。 常见公共方法---简单 运算符描述支持的容器类型合并字符串、列表、元组*复制字符串、列表、元组in元素是否存在字符串、列表、元组、字典not in元素是否不存在字符串、列表、元组、字典 案例&#xff1a; 合并 代码&#xff1a; # …

阿里云99元VS腾讯云88元,双11云服务器价格战,谁胜谁负?

在2023年的双十一优惠活动中&#xff0c;阿里云推出了一系列令人惊喜的优惠活动&#xff0c;其中包括99元一年的超值云服务器。本文将带您了解这些优惠活动的具体内容&#xff0c;以及与竞争对手腾讯云的价格对比&#xff0c;助您轻松选择最适合的云服务器。 99元一年服务器优…

使用SSH和SCP传输文件———详细入门教学实践

确保你已经在本地机器上安装了SSH客户端和SCP工具。 获取远程虚拟机的IP地址或主机名以及登录凭据&#xff08;用户名和密码或私钥&#xff09;。 打开终端&#xff08;命令提示符&#xff09;并输入以下命令来传输文件&#xff1a; scp /本地路径/文件 用户名远程虚拟机IP地…

ubuntu设置脚本开机自启动

rc-local.service flexmitd1:~$ cd /lib/systemd/system/ flexmitd1:/lib/systemd/system$ ls |grep rc-local.service rc-local.service rc-local.service.d flexmitd1:/lib/systemd/system$ pwd /lib/systemd/system flexmitd1:/lib/systemd/system$确保有rc-local.service文…

深入理解JMM(Java内存模型)

一、什么是JMM? Java内存模型(Java Memory Model简称JMM)是一种抽象的概念&#xff0c;并不真实存在&#xff0c;它描述的一组规则或者规范。通过这些规则、规范定义了程序中各个变量的访问方式。jvm运行的程序的实体是线程&#xff0c;而每个线程运行时&#xff0c;都会创建一…

C# - 委托、事件、Action、Func

前言&#xff1a;所有的名词&#xff0c;都是基于委托产生的 委托 &#xff08;delegate&#xff09; 解释&#xff1a; 其实就是一种指定格式的函数模版(容器) 这个模版(容器)可以用来存放各种格式和它相同的函数(的引用) 比如指定类型参数 指定参数个数 指定返回值等等 定义…

社区论坛小程序系统源码+自定义设置+活动奖励 自带流量主 带完整的搭建教程

大家好啊&#xff0c;又到了罗峰来给大家分享好用的源码的时间了。今天罗峰要给大家分享的是一款社区论坛小程序系统。社区论坛已经成为人们交流、学习、分享的重要平台。然而&#xff0c;传统的社区论坛往往功能单一、缺乏个性化设置&#xff0c;无法满足用户多样化的需求。而…

无人零售:创新优势与广阔前景

无人零售&#xff1a;创新优势与广阔前景 无人零售在创新方面具有优势。相比发展较为成熟的欧洲和日本的自动贩卖机市场&#xff0c;中国的无人零售市场人均占有量较少&#xff0c;这表明该市场具有广阔的前景和巨大的市场潜力。 此外&#xff0c;无人零售涉及到许多相关行业&…

GD32_ADC采样+DMA多通道扫描传输

GD32_ADC采样DMA多通道扫描传输 文章目录 GD32_ADC采样DMA多通道扫描传输前言一、资源介绍二、原理1.ADC连续扫描模式2.DMA传输3.ADC内部通道 三、配置1.ADC配置2.DMA配置3.注意事项 四、计算1.分压转换2.数据转换 前言 <1>、硬件平台&#xff1a;可运行软件程序的GD32单…

【算法】堆排序

算法-堆排序 前置知识 堆&#xff08;即将更新&#xff09; 思路 我们现在有一个序列&#xff0c;怎么对它排序&#xff1f; 这是一个非常经典的问题&#xff0c;这里我们使用一个借助数据结构的算法——堆排序解决。 这里有一个序列&#xff0c;要对它升序排序 4 7 3 6 5 …

前端开发好用的vscode插件

1.TONGYI Lingma 通义灵码&#xff0c;是一款基于通义大模型的智能编码辅助工具&#xff0c;提供行级/函数级实时续写、自然语言生成代码、单元测试生成、代码注释生成、代码解释、研发智能问答、异常报错排查等能力&#xff0c;并针对阿里云 SDK/API 的使用场景调优&#xff0…

云课五分钟-03第一个开源游戏复现-贪吃蛇

前篇 云课五分钟-02第一个代码复现-终端甜甜圈C 视频 云课五分钟-03第一个开源游戏复现-贪吃蛇 一个终端的动态字符显然很难调动编程的积极性&#xff0c;那么更有趣的开源的游戏也许是一种更好的启发。 文本 蓝桥ROS机器人之绚丽贪吃蛇 如何在Linux下使用 DungeonRush-mast…

【LeetCode】每日一题 2023_11_15 K 个元素的最大和(脑筋急转弯+数学)

文章目录 刷题前唠嗑K 个元素的最大和题目描述代码与解题思路 结语 刷题前唠嗑 LeetCode? 启动&#xff01;&#xff01;&#xff01; 首先声明一点啊&#xff0c;这个脑筋急转弯的题目标签可不是我想的啊&#xff0c;这个是 LeetCode 官方给这道题标注的啊 K 个元素的最大和…

贪吃蛇小游戏

一. 准备工作 首先获取贪吃蛇小游戏所需要的头部、身体、食物以及贪吃蛇标题等图片。、 然后&#xff0c;创建贪吃蛇游戏的Java项目命名为snake_game&#xff0c;并在这个项目里创建一个文件夹命名为images&#xff0c;将图片素材导入文件夹。 再在src文件下创建两个包&#…