Redis 异地双活实战

news2024/9/24 3:18:10

本文主要讲述异地双活方案redis的热备、双写、双向同步的区别和优劣势。并且说明了双写同步方案中redis集群主从数据同步的过程,以及中间件方案遇到的部分问题点,说明最终方案的实现思路和方案。

redis的双活方案无非有以下三种:热备,双写,双向同步。下文会说明三个方案的区别,并着重讲解双向同步的方案。

热备

其中,热备由于备用redis集群平时不会被使用,只有双活故障切换才会使用,且该方案redis备机需要实时数据同步,则切换后的稳定性较低、并且需要同时维护两套集群,成本也不低。两个IDC同连一个redis集群,假如某个IDC距离过大,则必然至少有一个IDC和redis集群距离过大,则该方案的延迟相比原生redis会大大增加(该延迟同redis双写的延迟)。因此在异地双活中基本不会考虑,可做为前期的同城双活方案的过度方案,本文不会详细说明。

双写

双写,顾名思义就是两个机房同时写入,即在双活项目中,一个redis的命令写入本机房redis集群的同时,会同时写入另外一个机房的redis集群。保证两个机房redis数据是一致的。如下图所示:

在我理解中,双写主要分为以下四个方式:同步双写、异步双写、redis集群维度同步双写、redis集群维度异步双写。

同步双写

同步双写,指的是同个redis命令同时写入两个机房,是同步执行的。 

例如:用户发了一个顺风车乘客订单,该订单被路由到中心机房(HZUNIT),且发单成功在业务上需要缓存订单数据到redis中,则该订单会先在中心机房(HZUNIT)写入一条缓存数据,同时会同步写入一条数据到异地的单元机房(SHUNIT)。

该方案存在以下几个问题:

维护成本

假如双写需要在业务代码层面维护,那代码侵入性会非常大。假如双写有个配置页面,配置需要双写的key,由中间件SDK实现双向,则每次新增或者修改双写的key都要去配置页面配置,后期维护中漏配风险非常大。

回滚问题

无论双写是在业务代码层面维护还是中间件SDK维护,那么当本机房操作成功,异地机房操作失败后,业务代码或者中间件代码需要考虑回滚逻辑,是跳过,还是回滚之前的命令,还是重试异地机房写入命令。如果选择重试,那一直失败如何处理,怎么保证数据一致性都是比较大的问题。

延迟

延迟是最致命的,众所周知redis的写入速度是非常高的,通常一个redis命令的写入是1ms以内,但是假如同步双写,则写入异地机房的时长会由两机房的物理距离决定,根据饿了么双活的测算,北京-上海的延迟大约为30ms,那这个异地的延迟是30倍,假设业务原先写入该redis命令的时间是1ms,那双写后变为31ms,显然这是无法接受的。

异步双写

异步双写,指的是同个redis命令写入两个机房,对本机房是同步写入的,对异地机房是异步写入的。 

例如:用户发了一个顺风车乘客订单,该订单被路由到中心机房(HZUNIT),且发单成功在业务上需要缓存订单数据到redis中,则该订单会先在中心机房(HZUNIT)写入一条缓存数据,同时会异步写入一条数据到异地的单元机房(SHUNIT)。

该方案同样存在以下几个问题:

维护成本

与同步双写方案问题一致。

回滚问题

大部分与同步双写方案问题一致,且由于异步,一致性更无法保证。

延迟

该方案由于异地机房是异步写入,因此延迟问题影响几乎没有。(如数据要求强一致,则不适合用异步双写)

集群同步双写

集群同步双写,指的是同个redis命令写入两个机房,是同步执行的。但是双写是redis集群维度的,即配置了该redis集群为双写,那么整个redis集群的任意写入命令都会进行同步双写操作。

该方案同样存在以下几个问题:

维护成本

该方案的维护成本只在于新增redis集群后需要配置一下,或者通知中间件,维护成本较低。

回滚问题

与同步双写问题一致。

延迟

与同步双写问题一致。

集群异步双写

集群异步双写,指的是同个redis命令写入两个机房,对本机房是同步写入的,对异地机房是异步写入的。但是双写是redis集群维度的,即配置了该redis集群为双写,那么整个redis集群的任意写入命令都会进行异步双写操作。

该方案同样存在以下几个问题:

维护成本

该方案的维护成本只在于新增redis集群后需要新增配置一下,或者通知中间件,维护成本较低。

回滚问题

与异步双写问题一致。

延迟

与异步双写问题一致。

结论

从以上说明中可以看出redis集群维度同步双写相对于同步双写,有了较低的维护成本,延迟问题和回滚问题无法解决。redis集群维度异步双写相对于异步双写,有了较低的维护成本,回滚问题仍旧无法解决。无论同步双写还是redis集群维度同步双写延迟问题都是比较大的,因此这两个方案都最好不用。异步双写和redis集群维度异步双写,数据一致性问题则是一个大问题,因此该方案在某些数据一致性的场景下也不可用。基于以上两种方案,redis双向同步方案应运而生。

双向同步

参考:redis设计与实现

redis双向同步,即redis写入代码在业务侧无任何区别,还是只写入本机房,只是底层通过中间件同步任务,同步到异地机房。如下所示:

该方案也会有数据一致性问题,但是该方案的数据同步是中间件双向同步应用伪装为redis集群的slave节点,因此一致性的保证和redis集群的一致性保障是一致的,基本上能满足双活需求。

概念说明

复制偏移量(offset)

  • 执行主从复制的双方都会分别维护一个复制偏移量

  • master 每次向 slave 传播 N 个字节,自己的复制偏移量就增加 N

  • slave 接收 N 个字节,自身的复制偏移量也增加 N。

  • 通过对比主从之间的复制偏移量就可以知道主从间的同步状态

  • offset会一直自增,不会到backlog的size停下从0开始

复制积压缓冲区(backlog)

  • 为了解决断点重连全量同步问题设计

  • master往slave同步数据时,会将数据往backlog也写一份

  • 当master和slave网络抖动重新连接后,slave会将自己的offset发送给master

  • master对比slave的offset和backlog的offset

  • 如果slave的offset在backlog,则将复制缓冲区当前的offset之后的数据同步给slave

  • 如果slave的offset不在backlog中,则进行全量同步

运行id(runid)

  • 基于backlog逻辑缺陷设计

  • 每个redis server无论主从都有自己的runid

  • 由40位随机16进制字符组成。如:53b9b28df8042fdc9ab5e3fcbbbabff1d5dce2b3

  • 如slave断网重连后,slave会将offset和当前保存的runid发送给master

  • master对比slave传入的runid和自己的runid是否一致,从而判断是否同一个master

redis大版本解决问题

sync-->psync1-->psync2的进化

  • redis2.8版本以下:无论是第一次主从复制还是断线重连后再进行复制都采用全量同步,因此如果redis主从节点网络抖动频繁,则会导致频繁的全量同比,导致整个集群压力较大。(同步方式sync)

  • redis2.8-4.0以下版本:支持断点续传,但是master切换或者复制积压缓冲区满了以后,无法断点续传。(同步方式psync1)

  • redis4.0及以上版本:支持master切换的断点续传,但是复制积压缓冲区满了也无法支持断点续传。(同步方式psync2)

sync

redis2.8版本以下,通过sync命令进行同步,集群无runid,backlog和offset,因此每次网络抖动导致的redis主从节点断开,等到主从节点重新连接后,都会进行全量的数据同步,现在该版本应该几乎没有在使用了,本文不做具体描述。

psync1

为了解决redis的断点续传而设计,通过runid和offset以及backlog,从而记录之前连接断开或者故障前的同步进度,等重新连接后根据offset和backlog断点续传。 

无法解决问题:

  • 断点后新写入的数据完全覆盖backlog,导致原先断开连接前的offset不在backlog中

  • 主从切换后,由于主节点更换,也会引发全量同步

数据同步过程

数据全量同步过程如下所示:

  • 当主从重新连接后,从服务器会告诉主服务器,是否是第一次执行,如果是的话,则向主服务器发送psync ? -1 命令,该命令代表从服务器告诉主服务器需要全量同步,则主服务器直接会把全量数据推给从服务器

  • 如果从服务器告诉主服务器是断点续传,则会向主服务器发送psync 命令,告诉主服务器需要断点续传

  • 当主服务判断offset不在backlog中,或者runid和自己id不一致,则会告诉从服务器需要进行全量同步,并把全量数据返回给从服务器

  • 当主服务判断offset在backlog中,或者runid和自己id一致,则会进行断点续传

下图from:redis设计与实现

psync1命令的主从全量同步

当redis的主从关系是新的,会进行全量同步,或者之前执行过slaveof no one命令,全量同步的过程如下:

  • slave从未任何master同步过数据(新的slave)或者是之前执行过slaveof no one命令,在开始新的一次复制之时向master发送psync?-1命令主动请求全量同步

  • master执行bgsave,生成rdb文件,并发送数据到slave

  • master返回+FULLRESYNC和master的runId以及当前的offset

  • slave保存runId,并且将offset作为自己的初始化偏移量

psync1的增量同步(断点续传)

正常

  • 正常情况下,redis主从同步是从主节点同步给从节点。(主节点发送的时候会带上runid和offset)

  • 在同步过程中,master新写入的数据,会在backlog保存一份

异常

如因为网络原因导致连接异常,slave会自动请求master重新建立连接。

  • slave发送connect

  • slave发送psync runid offset

  • 判断offset是否在backlog

  • offset在backlog中:是的话会将backlog里面offset为slave offset之后的全部数据进行通报

  • offset不在backlog中:进行全量同步

psync2解决的问题

  • psync2主要是引入了replid(同runid)和replid2(之前的master的replid)

  • slave的复杂积压缓冲区解决HA的全量同步问题

  • slave的复制积压缓冲区作用,切换成master后,也有offset

  • replid2的作用可以判断新的master和新的slave是否之前是一个集群,如果是一个集群则可以根据offset进行数据同步

  • 主从切换的时候,根据replid2和offset进行增量同步

  • 但是slave的offset大于当前backlog的最大offset,或者小于当前backlog的最小offset(不在backlog中)还是会进行全量同步

前面主要是说明redis本身集群的主从数据是如何同步的,而中间件的双向同步则是基于伪装未主从的slave进行的数据同步,后面则着重说明多主多从的情况下,数据是如何分配到正确的节点(卡槽)

卡槽判断逻辑

下图from:redis设计与实现

三主三从集群数据同步

以三主三从为例,数据分配节点和同步的过程如下:

  • 客户端执行set k.v命令进行写入redis cluster(集群)

  • 假如master1在集群初始化承接的slotid为0-5000,master2为5001-10000,master3位10001-16383

  • redis cluster对key进行CRC16算法对16384取模,分别落入响应的master1或者master2或者master3

  • 假如取模后的值为4500,则会写入master1,而master1会将该命令发送给slave1

中间件方案

伪装slave

  • 伪装的slave节点交互与 master 的交互细节

  • 发送 AUTH {password}

  • 发送 REPLCONF listening-port {port} , 告知master自己的监听端口

  • 发送 REPLCONF ip-address {address}, 告知master自己的ip

  • 发送 REPLCONF capa eof (使用 diskless-replication)

  • 发送 REPLCONF capa psync2

  • 发送 PSYNC {repl_id} {repl_offset} , 发送复制id(master runid) 和自己复制进度offset

  • 接收 Binary data

  • 接收 aof data

双向同步方案

redis的双写同步方案是基于两个单元同步完成的,是一个单元到中心的单向同步和中心到单元的单向同步,两个合并为一个双写同步方案。

基本说明

  • center_cluster代表中心机房的redis的集群

  • center_master_node代表中心机房的redis的master节点

  • unit_cluster代表单元机房的redis的集群

  • unit_master_node代表单元机房的redis的master节点

  • center_replication代表中间件中心到单元的同步任务

  • unit_replication代表中间件单元到中心的同步任务

同步过程

以客户端写入在中心机房,同步到单元机房为例

  • 客户端执行写入命令set k.v

  • 该命令被分配到中心机房,center_cluster

  • 该命令根据slotid分配到对应的master节点

  • master节点会向中间件伪装成的slave节点发送数据,即向center_replication单向同步任务发送数据

  • center_replication同步任务会同步到unit-cluster

  • 后续则为unit-cluster单元机房redis集群自己的数据同步

问题--如何防循环复制

从同步过程可以看出,该双向同步是个环装,还是以上面例子为例,当center-master-node同步数据到unit-cluster后,unit-cluster也会有个unit-replication监听unit-master-node,进行同步到中心机房,则该命令会进行循环同步。

解法

通过固定前缀的防循环复制命令key,防止循环复制。

由于redis单线程的特性,在center-replation接收到主节点的命令后,会在set k,v命令后紧接跟着set一个固定前缀的key值(防循环复制key)。在unit-repliation接收到该节点的命令后,会getNext(),判断next命令是否防循环复制命令,如果是防循环复制key,则unit-repliation不会进行同步,否则的话,unit-repliation会进行同步。

问题:如何保证redis的原始命令的key和防循环复制的命令的slotid相同(不同则getNext会拿到非防循环命令,导致无法防循环复制)。 

答:通过hashTag保证,具体可查询资料。

单向同步任务

单元同步任务是根据源和目标设置的,源即代表数据源的节点,该节点为单个master节点,目标即为目标的redis集群节点,为一个集群。即如果中心机房有三个主节点,则做中心到单元的单向同步,需要配置三个同步任务,每个同步任务的源节点为master1、master2、master3。目标节点均为正规单元机房redis集群。 

问题:为何目标节点为redis集群?

因为单元和中心机房的主从数量不是一比一的,比如中心机房为三主三从,单元机房为2主2从。那么set k,v命令在中心机房属于master3,则在单元机房是属于master1或者master2。因此需要将目标数据同步到集群中,由集群的slot自行判断落入哪个master。

退一步说,即使中心机房为3主3从,单元机房也为3主3从,那么中心机房的master1不代表一定对应单元机房的master1,因为slotid的范围是和每个节点的性能也是相关的。

双向同步完整方案

以下是我自己画的redis双向同步完整方案,如下所示。

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

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

相关文章

是否只能搞底层才能成为技术大神?

hi,大家好,我是大师兄alex,想必大家经常听到,想要长远发展,必须要往底层走,技术大神都是搞底层的,你会看到很多人一旦想变得硬核,都喜欢展现自己搞过一些底层技术,比如体…

配置热更新/支持 Reload、QUIC 桥接再升级

12 月,NanoMQ 继续保持稳步更新,最新的 0.15 版本将于本月初发布。这一版本增加了配置热更新功能和 Reload 命令;MQTT over QUIC 桥接再次得到升级,增加了拥塞控制和 QoS 消息优先传输;另外也为上一个版本新增的 HOCON…

2003-2021年高铁线路信息数据

2003-2021年高铁线路信息数据 1、时间:2003-2021年 2、指标: 高铁线路名称、起点名、终点名、开通时间、线路长度(km)、设计速度(km/h)、沿途主要车站 3、指标说明: 高铁一般指高速铁路。 高速铁路,简称高铁&…

基于 K8s 的 MySQL 数据持久化存储

目录一、步骤二、实践2.1 创建 PV 和 PVC2.2 部署 MySQL 服务2.3 创建测试数据2.4 模拟节点宕机2.5 数据一致性验证FAQ一、步骤 (1)创建 PV 和 PVC。 (2)部署 MySQL 服务。 (3)向 MySQL 添加数据。 &am…

什么是Vue、Vue开发的方式、Vue的基本指令

一、什么是Vue 1. 前端技术的发展(html、CSS、JavaScript) ​ (1)jQuery:是对JavaScript进行了封装,使得操作DOM、事件处理、动画处理、ajax交互变得非常简洁、方便。是JavaScript的库。 ​ &#xff08…

贴吧低代码高性能规则引擎设计

作者 | 贴吧UEG技术组 导读 本文首先介绍了规则引擎的使用场景,引出贴吧规则引擎。从组件、变量、规则、处置四个模块介绍了规则引擎的组成部分,同时对最终规则文件的编译过程做了详细介绍。为了做到低代码,在规则配置上做到平台化&#xff0…

2022第十一届中国创新创业大赛全国总决赛在深圳举办

为深入贯彻党的二十大精神和创新驱动发展战略,强化企业科技创新主体地位,支持企业融合创新,推进科技型创新型企业遴选和培育,加大金融对硬科技企业的支持力度,营造有利于科技型中小微企业成长的环境,推动创…

Nuxt.js

Nust.js介绍 Nust.js介绍 移动互联网的兴起促进了web前后端分离开发模式的发展,服务端只专注业务,前端只专注用户体验,前端大量运用的前端渲染技术,比如流行的vue.js、react框架都实现了功能强大的前端渲染。但是,对…

深入探索Linux虚拟化KVM-Qemu分析之CPU虚拟化

说明: KVM版本:5.9.1QEMU版本:5.0.0工具:Source Insight 3.5, Visio 1. 概述 本文围绕ARMv8 CPU的虚拟化展开;本文会结合Qemu KVM的代码分析,捋清楚上层到底层的脉络;本文会提供一…

MYSQL对千万级数据的快速迁移

MYSQL对千万级数据(10个G左右)的迁移,耗时控制在半小时内 1.show variables like %secure%;查看 secure-file-priv 当前的值是什么 2.如果没有可以在my.ini(windows)my.cnf(linux)中添加secure…

得物染色环境落地实践

1. 背景 测试环境治理一直是各大公司非常重要的一个课题,测试环境稳定性很大程度影响迭代开发&测试效率。 综合来看,测试环境不稳定的原因主要有以下几点: 测试环境的变更非终态变更,经常会有代码发布/配置发布导致服务无法…

ArcGIS基础实验操作100例--实验47融合相邻面要素

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台:ArcGIS 10.6 实验数据:请访问实验1(传送门) 高级编辑篇--实验47 融合相邻面要素 目录 一、实验背景 二、实验数据 三、实验步骤 (1&am…

【Vue3】Vue内置指令

注意&#xff1a;在vue中使用v-开头的都是Vue指令。一、v-html插入标签可以识别标签。<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"&…

argparse简介

一、argparse简介 argparse 模块是 Python 内置的用于命令项选项与参数解析的模块&#xff0c;argparse 模块可以让人轻松编写用户友好的命令行接口&#xff0c;能够帮助程序员为模型定义参数。 argparse定义四个步骤 导入argparse包 ——import argparse创建一个命令行解析…

Fastjson反序列化审计及验证

简介&#xff1a;数字化平台已经与我们生活紧密结合&#xff0c;其用户规模庞大&#xff0c;一旦系统出现故障&#xff0c;势必会造成一定生活的不便。比如疫情时代&#xff0c;健康码已经成为人们出门必备的条件&#xff0c;一旦提供健康码服务平台出现故障&#xff0c;出行将…

imx6ull内核移植记录:failed to find ocotp node

前言 记录记录&#xff0c;是个和MAC地址相关的信息。 一 尝试尝试 [ 0.093958] vddsoc: supplied by regulator-dummy [ 0.126215] failed to find ocotp node [ 0.126744] No ATAGs? 经过分析&#xff0c;拆分为两个问题&#xff1a; [ 0.126215] failed t…

Web前端 CSS3 02 小练习

京东小项目 设置一个京东侧边栏 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content&quo…

读 NebulaGraph源码 | 查询语句 LOOKUP 的一生

本文由社区用户 Milittle 供稿 LOOKUP 是图数据库 NebulaGraph 的一个查询语句。它依赖索引&#xff0c;可以查询点或者边的信息。在本文&#xff0c;我将着重从源码的角度解析一下 LOOKUP 语句的一生是如何度过的。 本文源码阅读基于内核源码的 v3.3.0 版本&#xff0c;详见 …

实现SQLSERVER 调用 C# 代码

简单例子 首先写一段简单的 C# 代码&#xff0c;然后把它编译成 dll。 namespace Bussiness {public class UserFunctions{public static string UserLogin(string username, string password){var random new Random();var isSuccess random.Next() % 2 0;return isSucce…

【漏洞复现】钉钉rce反弹shell

文章目录 一、漏洞描述二、漏洞原理三、影响版本四、复现过程0.环境说明1.msf 生成shellcode2.msf开启监听3.将生成的shellcode替换原shellcode4.开启web服务&#xff0c;并上传poc文件&#xff0c;构造poc5.从钉钉发送poc给受害者6.受害者点击即会触发漏洞&#xff0c;在msf监…