当前位置: 移动技术网 > IT编程>数据库>Redis > Redis异地多活原理简介

Redis异地多活原理简介

2020年07月17日  | 移动技术网IT编程  | 我要评论

一、异地多活需要了解的知识

  1. 什么是异地多活
    简单来说,就是在不同地域建立数据中心,每个数据中心在日常使用中都需要正常接入业务流量,做业务支撑。
  2. 异地多活的优势
    主要体现在以下几个方面。
  • 单机房系统架构,一旦机房故障,线上业务中断将会带来不可估量的影响。但是对于异地多活架构的系统来说,如下图:
    在这里插入图片描述
    其中地域 1 机房故障,甚至地域 2 机房也故障,只要地域 3 的机房是可用的,流量都可以切到地域 3 机房,线上的业务也不会中断。

  • 性能的提升

举个简单的例子:从北京到上海共 1468 公里,即使是光速传输,一个来回也需要接近 10ms;实际测试中,延迟大概在 30ms 左右。但是如果有多个数据中心,可供客户端就近访问,那么性能可能提升几个量级

异地多活,也属于分布式架构的系统。也绕不开CAP

C: 一致性(Consistency ),在分布式系统中的所有数据备份,在同一时刻是否同样的值。
A: 可用性( Availability ),在集群中一部分节点故障后, 集群整体是否还能响应客户端的读写请求。
P: 分区容错性( Partition tolerance ),以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在 C 和 A 之间做出选择。

二、原生 Redis 做异地多活所面临的的问题

  • 断点续传能力差

redis 增量重传过程,如下图所示:
在这里插入图片描述

从节点链接主节点后,会向主节点发送psync 命令,主节点收到命令后,查找对应的 replid的复制积压缓冲区(backlog),如果所需同步的 offset 大于 server 记录的 offset,增开始增量同步;否则,开始全量同步

4.0 版本: replid 与 offset 持久化在 rdb 中,重启/主备切换后可以续传,但是这些仅仅是减小了全量重传的概率,不能解决全量重传的问题。
由上可知,是否全量重传,需要靠 backlog 来保证,但是 backlog的大小受限且不宜过大。

  • 双活时无法进行全量同步
    众所周知,Redis 是一个单线程的程序,在原生版本全量同步过程中,所有的请求都会阻塞。在可用性要求比价高的系统中,这是无法被容忍的。所以使用原生 redis 在多活系统中,一个数据中心的 redis 集群在全量同步过程中,会造成所以请求阻塞并中断,导致业务调用失败。

  • 无法去环
    一个多活集群,就像一个小型的环形网络,如下图所示。
    在这里插入图片描述
    当同步程序从地域 1 集群向地域 2 集群同步set key val命令,地域 2 集群写入同步程序发送过来的命令,接着地域 2 的同步程序又向地域 3 的集群发送当前变更命令;同理,地域 3 的同步程序又向地域 1 的集群同步,以此类推,这个数据变更同步会在多个集群间无限循环下去。

  • 可扩展性差,实现多活架构复杂
    原生 redis,还是可以借助三方组件,通过修改客户端的方式,来实现异地多活。

例子: 快手自研的客户端修改方案 (额。。内部WIKI 就不贴地址了)

实现的基本原理,是在客户端将命令变更发送到 kafka,再由同步程序将变更从 kafka 消费然后写入目标集群,从而实现数据同步。所以,理论上需要对所有的 redis 的客户端做一次封装,维护成本过大,不利于其他未封装客户端的接入;且该架构严重依赖 kafka 消息中间件。

同步方案设计

考虑到 redis 大部分作为纯缓存的应用场景,在设计异地多活时,为了保证缓存的高可用性,防止因校验数据一致带来的性能损耗甚至集群不可用导致缓存击穿,所以我们将 redis 异地版本,设计为优先遵循 AP 原则,既一个地域集群实例故障再恢复时,同一时段内会适当容忍部分数据不一致的情况,但保持最终数据一致性。

  1. 内核层面解决断点续传、双活全量同步、回环

(1) AOF 的特性与优势
特性:
Redis 所有变更操作写入 AOF 文件
AOF 文件都是追加循序写
修改redis协议
追加顺序写,很容易优化写入性能(MMAP,PageCache 等)
RESP 协议短小精悍,便于改造优化
基于 AOF,可以通过增加协议字段的方式,实现众多功能

(2) 解决数据回环问题
在RESPv2 协议前增加 3 个标志位:

server-id : 对应 redis 实例全局唯一的 id
opid : redis 变更操作编号
Src-id : 源 redis 实例 id,用于区分哪些数据是接收同步过来的,哪些是自己的
协议整体设计格为 opinfo_cmd + real_cmd,仅在写 AOF 文件时,插入对应的 opinfo_cmd;

例如:
serverA 写入了一条编号为 101 的变更操作,随后收到同步程序发送的 serverB 的相关变更数据;
收到 serverB 发送的数据后,经过一系列校验,写入 AOF 文件,将 src_opid 置为对应的 src_opid;
同步程序发送增量数据时,直接略过 server_id 不为 A 且 src_opid 不等于-1 的数据即可,这样就从源头上,解决了数据回环问题。

(3) 增强断点续传能力
为了增强异地多活版本的断点续传能力,协议中又增加了一个 timpstamp 字段,使得可以多维度断点续传

(4)解决双活无法全量同步
针对这个问题,我们特意优化了同步程序,现有同步程序流程如下:

0131b9d7-e2ef-4877-a6b4-2956487cac10

同步程序收到 rdb 文件后,直接解析 RDB 文件,通过 redis Restore 命令进行恢复;恢复成功后,通过我们新增的 opget 命令,拉取增量数据,进行同步。这样,就不会使对端 redis 中断。

解决多活一致性问题 - CRDT
CRDT(Conflict-Free Replicated Data Type)是各种基础数据结构最终一致算法的理论总结,能根据一定的规则自动合并,解决冲突,达到强最终一致的效果。CRDT 详细介绍

(1)符合 CRDT 操作的 3 个基本原理
结合律:(X U Y) U Z = X U (Y U Z)
交换律:X U Y = Y U X
幂等律:X U X = X
(2)Redis - CRDT 命令原理概览
Counter 类 - Redis 对应的命令操作(incr,decr,incrby,decrby)
INCRY,天然符合结合律与交换律,但是不符合幂等律,所以在写入时必须保证不丢不重,才能保证最终结果的一致。
Register 类 - Redis 对应的命令操作(set)
set 不满足交换类,所以改造时需要增加一些附加的元信息,来保证更新的单调性。这里采用 LWW Register,既 Last-Write-Win,通过附加时间戳信息,来保证所有异地集群,都是最后写入的最新数据。
在这里插入图片描述
Set 类 - Redis 对应命令操作(集合类命令、Hash 命令)
多点并发进行 Add 与 Remove 操作时,因为无法满足交换律,所以会有冲突,如下图所示,最后经过同步,两边集群数据不一致,且最终结果与预期均不符

解决此类问题,从学术原理角度,有 LWW-element-Set、Observed-Remove Set 等方案;但是从工程角度,LWW-element-Set 更加易于实现与维护,其原理是在 Set 中增加时间戳,具体原理实现流程如图:

参考:

  • CRDT: https://hal.inria.fr/inria-00609399/document
  • 阿里云CRDT介绍 https://developer.aliyun.com/article/635632
  • 快手Stack: https://kstack.corp.kuaishou.com/tech/web/article/info/812

本文地址:https://blog.csdn.net/qq_33797928/article/details/107350683

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网