一文搞懂 Redis 架构演化之路(腾讯技术工程)

引言

redis是目前最流行的缓存中间件,但他是如何保证实现稳定并且高性能的提供服务的?

  1. 如果只是单机版redis,有什么问题?
  2. 宕机后,如何快速恢复?
  3. 主从集群以及sentinel可以带来怎样的优势?
  4. 分片集群作用?

这篇文章从0到1,一步步构建出当前的redis

单机redis

image-20240402113405372

如上图所示,项目的数据最简单的就是直接从mysql读取,现在多了一个redis缓存,并使用cache aside pattern。

在服务不断运行中,项目中redis存储的数据越来越多,如果突然redis宕机,由于redis是内存数据库,全部的数据都会丢失。

  1. 读数据:就算redis重启了,数据丢失了,构建缓存的过程中数据库压力很大
  2. 写数据:如果有写数据还没有写入到数据库,数据就永久丢失了

持久化:有备无患

AOF

image-20240402113736587

最简单的持久化操作就是:每执行一个命令,除了更新redis内存外,还写入磁盘,也就是磁盘和redis始终同步。

问题:写磁盘的速度肯定是跟不上redis,如果希望强一致性,那磁盘的读写速度就是redis的性能极限,性能急剧下降。

数据从内存到磁盘分两步:

  1. 程序写文件的 PageCache(write)
  2. 把 PageCache 刷到磁盘(fsync)

image-20240402114411870

刚才是每次都执行这两步,现在可以只执行第一步,然后让后台线程去执行fsync操作

image-20240402114515651

其实这就是AOF,(Append Only File)。

Redis AOF 持久化提供了 3 种刷盘机制:对应着fsync的不同时机

  1. appendfsync always:主线程同步 fsync
  2. appendfsync no:由 OS fsync
  3. appendfsync everysec:后台线程每间隔1秒 fsync;大大减小了io的频率

​ 随着时间推移,AOF文件可能越来越大,redis还提供了AOF rewrite使得AOF【瘦身】,如 set k1 v1,set k1 v2,其实我们只需要记录后一条命令就好了(最终版本)

image-20240402114719479

RDB

除了使用基于命令的方式,还可以使用最常用的基于数据快照实现数据的备份

快照的备份时机可以定时的,也可也是最近一段时间数据的修改量 save 300 10 # 100s内有10个key修改了则触发bgsave

image-20240402115227024

优点

  1. 持久化体积小(二进制+压缩)
  2. 写盘频率

缺点也很明显,因为是定时持久化,数据肯定没有 AOF 实时持久化完整。

如果你的 Redis 只当做缓存,对于丢失数据不敏感(可从后端的数据库查询),那这种持久化方式是非常合适的。

如果让你来选择持久化方案,你可以这样选择:

  1. 业务对于数据丢失不敏感,选 RDB
  2. 业务对数据完整性要求比较高,选 AOF 但文件体积更大、恢复更慢

混合持久化

如何保证数据完整性,又可以让文件更小(恢复更快)呢?

数据完整性:想要保住数据完整性,就需要从AOF下手(记录下每条命令),还想要体积更小,就将RDB快照(二进制+压缩)嵌入到AOF中

image-20240402142442717

当 AOF 在做 rewrite 时,Redis 先以 RDB 格式在 AOF 文件中写入一个数据快照,再把在这期间产生的每一个写命令,追加到 AOF 文件中。

image-20240402130926543

数据恢复时,先加载AOF中的RDB,再执行命令

现在已经实现单机的恢复,但恢复启动的过程中服务还是宕机的,解决方法:引入多个 Redis 实例,这些实例实时进行同步,当一个宕机后剩下的立马可以补救【主从复制:多副本】

主从复制:多副本

img

  1. 缩短不可用时间:master 发生宕机,我们可以手动把 slave 提升为 master 继续提供服务
  2. 提升读性能:让 slave 分担一部分读请求,提升应用的整体性能

但它的问题在于:当 master 宕机时,我们需要「手动」把 slave 提升为 master,这个过程也是需要花费时间的。

优化:切换的过程,变成自动化?

哨兵:故障自动切换

image-20240402132002932

  1. 引入哨兵实现服务的监控
  2. 多个哨兵避免网络问题引起的波动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
哨兵协商判定 master 异常后,这里还有一个问题:由哪个哨兵来发起主从切换呢?

答案是,选出一个哨兵「领导者」,由这个领导者进行主从切换。

问题又来了,这个领导者怎么选?

想象一下,在现实生活中,选举是怎么做的?

是的,投票。

在选举哨兵领导者时,我们可以制定这样一个选举规则:

每个哨兵都询问其它哨兵,请求对方为自己投票
每个哨兵只投票给第一个请求投票的哨兵,且只能投票一次
首先拿到超过半数投票的哨兵,当选为领导者,发起主从切换
这个选举的过程就是我们经常听到的:分布式系统领域中的「共识算法」。

什么是共识算法?

我们在多个机器部署哨兵,它们需要共同协作完成一项任务,所以它们就组成了一个「分布式系统」。

在分布式系统领域,多个节点如何就一个问题达成共识的算法,就叫共识算法。

在这个场景下,多个哨兵共同协商,选举出一个都认可的领导者,就是使用共识算法完成的。

这个算法还规定节点的数量必须是奇数个,这样可以保证系统中即使有节点发生了故障,剩余超过「半数」的节点状态正常,依旧可以提供正确的结果,也就是说,这个算法还兼容了存在故障节点的情况。

共识算法在分布式系统领域有很多,例如 Paxos、Raft,哨兵选举领导者这个场景,使用的是 Raft 共识算法,因为它足够简单,且易于实现。

目前解决的问题:

  1. 数据怕丢失:持久化(RDB/AOF)
  2. 恢复时间久:主从副本(副本随时可切)
  3. 手动切换时间长:哨兵集群(自动切换)
  4. 读存在压力:扩容副本(读写分离)
  5. 写存在压力一个 mater 扛不住怎么办?

集群

image-20240402133049163

引入多个master分担压力

我们制定规则如下:

  1. 每个节点各自存储一部分数据,所有节点数据之和才是全量数据
  2. 制定一个路由规则,对于不同的 key,把它路由到固定一个实例上进行读写

需要一个路由

image-20240402133114498

这种方案也叫做「客户端分片」,这个方案的缺点是,客户端需要维护这个路由规则,也就是说,你需要把路由规则写到你的业务代码中。

如何做到不把路由规则耦合在客户端业务代码中呢?

继续优化,我们可以在客户端和服务端之间增加一个「中间代理层」,这个代理就是我们经常听到的 Proxy,路由转发规则,放在这个 Proxy 层来维护。使得proxy后面的一切对客户端来说都是透明无感知的

业界开源的 Redis 分片集群方案,例如 Twemproxy、Codis 就是采用的这种方案。

image-20240402133147080

官方

官方方案更加简单,无需路由以及哨兵

image-20240402133237153

  1. 故障监控:Redis Cluster 无需部署哨兵集群,集群内 Redis 节点通过 Gossip 协议互相探测健康状态,在故障时可发起自动切换。
  2. 路由:访问集群的任意节点,会自动转发到正确节点

虽然省去了哨兵集群的部署,维护成本降低了不少,但对于客户端升级 SDK,对于新业务应用来说,可能成本不高,但对于老业务来讲,「升级成本」还是比较高的,这对于切换官方 Redis Cluster 方案有不少阻力。

于是,各个公司有开始自研针对 Redis Cluster 的 Proxy,降低客户端的升级成本,架构就变成了这样:

Image

这样,客户端无需做任何变更,只需把连接地址切到 Proxy 上即可,由 Proxy 负责转发数据,以及应对后面集群增删节点带来的路由变更。

至此,业界主流的 Redis 分片架构已经成型,当你使用分片集群后,对于未来更大的流量压力,也都可以从容面对了!

总结

总结一下,我们是如何从 0 到 1,再从 1 到 N 构建一个稳定、高性能的 Redis 集群的,从这之中你可以清晰地看到 Redis 架构演进的整个过程。

  1. 数据怕丢失 -> 持久化(RDB/AOF)
  2. 恢复时间久 -> 主从副本(副本随时可切)
  3. 故障手动切换慢 -> 哨兵集群(自动切换)
  4. 读存在压力 -> 扩容副本(读写分离)
  5. 写存在压力/容量瓶颈 -> 分片集群
  6. 分片集群社区方案 -> Twemproxy、Codis(Redis 节点之间无通信,需要部署哨兵,可横向扩容)
  7. 分片集群官方方案 -> Redis Cluster (Redis 节点之间 Gossip 协议,无需部署哨兵,可横向扩容)
  8. 业务侧升级困难 -> Proxy + Redis Cluster(不侵入业务侧)

至此,我们的 Redis 集群才得以长期稳定、高性能的为我们的业务提供服务。

参考