关于redis:一文读懂-Redis-架构演化之路

45次阅读

共计 6884 个字符,预计需要花费 18 分钟才能阅读完成。

导语|近年来,Redis 变得越来越风行。Redis 长久化、主从复制、哨兵、分片集群是开发者常遇到的、看似容易了解的概念。它们存在什么分割?Redis 为什么会演化出几种架构模式?腾讯云后盾开发工程师谭帅将带你一步步构建出稳固、高性能的 Redis 集群。理解 Redis 做了哪些计划来实现稳固与高性能之后,你在日常应用 Redis 时,可能更加熟能生巧。

目录

1 情境引入:单机版 Redis

2 数据长久化:有恃无恐

3 主从复制:多正本

4 哨兵:故障主动切换

5 分片集群:横向扩大

6 总结

现如今 Redis 变得越来越风行,在很多我的项目中被用到。开发者们在应用 Redis 时,经常思考一个问题:Redis 是如何稳固、高性能地提供服务的?各位开发者也能够尝试答复一下以下这些问题:

● 我应用 Redis 的场景很简略,只应用单机版 Redis 会有什么问题吗?

● 我的 Redis 故障宕机了,数据失落了怎么办?

● 如何能保障我的业务、利用不受影响?

● 为什么须要主从集群?它有什么劣势?

● 什么是分片集群?我真的须要分片集群吗?

如果你曾经对 Redis 有些许理解,必定据说过数据长久化、主从复制、哨兵这些概念。它们之间又有什么区别和分割呢?如果你存在这样的纳闷,这篇文章会从 0 到 1、再从 1 到 N,带你一步步构建出一个稳固、高性能的 Redis 集群。

在这个过程中,你能够理解到 Redis 为了做到稳固、高性能所采取的优化计划及其起因。把握了这些原理,你在日常应用 Redis 时,可能做到加倍熟能生巧。

01、情境引入:单机版 Redis

首先,咱们从最简略的场景来假如,以便各位由浅入深了解。当初你有一个业务、利用,须要引入 Redis 来进步利用的性能。你能够抉择部署一个单机版的 Redis 来应用。就像这样:

这个架构非常简单,你的业务、利用能够把 Redis 当做缓存来应用。从 MySQL 中查问数据,写入到 Redis 中。业务、利用再从 Redis 中读取这些数据。Redis 的数据都存储在内存中,所以速度飞快。

如果你的业务体量并不大,那这样的架构模型根本能够满足你的需要。是不是很简略?随着工夫的推移,你的业务体量逐步倒退起来了,Redis 中存储的数据也越来越多,此时你的业务、利用对 Redis 的依赖也越来越重。

忽然有一天,你的 Redis 因为某些起因宕机了,你的所有业务流量都会打到后端 MySQL 上。这会导致你的 MySQL 压力剧增,重大的话甚至会压垮 MySQL。

这时你应该怎么办?我猜你的计划是:连忙重启 Redis,让它能够持续提供服务。然而,因为之前 Redis 中的数据都在内存中,只管你当初把 Redis 重启了,之前的数据也都失落了。重启后的 Redis 尽管能够失常工作,然而 因为 Redis 中没有任何数据,业务流量还是都会打到后端 MySQL 上,MySQL 的压力还是很大

这可怎么办?你陷入了深思。有没有什么好的方法解决这个问题?既然 Redis 只把数据存储在内存中,那是否能够把这些数据也写一份到磁盘上呢?如果采纳这种形式,当 Redis 重启时,咱们把磁盘中的数据疾速复原到内存中,它就能够持续失常提供服务了。这是一个很好的解决方案。把内存数据写到磁盘上的过程,就是「数据长久化」。

02、数据长久化:有恃无恐

当初,你构想的 Redis 数据长久化是这样的:

然而,数据长久化具体应该怎么做呢?我猜你想到的计划是:Redis 每一次执行写操作,除了写内存之外,同时也写一份到磁盘上。就像这样:

没错,这是最简略间接的计划。但认真想一下,这个计划有个问题:客户端的每次写操作,既须要写内存,又须要写磁盘。写磁盘的耗时相比于写内存来说要大很多。这势必会影响到整体写性能

如何躲避这个问题?咱们能够这样优化:Redis 写内存由主线程来做,写内存实现后就给客户端返回后果。Redis 用另一个线程去写磁盘,这样就能够防止主线程写磁盘对性能的影响。这的确是一个好计划。除此之外,咱们能够换个角度,思考一下还有什么形式能够长久化数据?各位读者能够联合 Redis 的应用场景来思考。

回顾一下:咱们在应用 Redis 时,通常把它用作什么场景?那就是「缓存」。把 Redis 当做缓存来用的话,只管 Redis 中没有保留全量数据,咱们的业务、利用仍旧能够通过查问后端数据库失去那些不在缓存中的数据。只不过查问后端数据的速度会慢一点,但对业务后果没有影响。

基于这个特点,Redis 数据长久化还能够用「数据快照」的形式来做。什么是数据快照呢?你能够这么了解:把 Redis 设想成一个水杯,向 Redis 写入数据,就相当于往这个杯子里倒水;此时你拿一个相机给这个水杯拍一张照片,拍照的这一瞬间,照片记录到这个水杯中水的容量,就是水杯的数据快照。

也就是说,Redis 的数据快照是记录某一时刻 Redis 中的数据,之后只须要把这个数据快照写到磁盘上就能够了。它的劣势在于:须要长久化时,把数据一次性地写入磁盘即可,其它工夫都不须要操作磁盘。基于这个计划,咱们能够定时给 Redis 做数据快照,把数据长久化到磁盘上。

其实,下面说的这些长久化计划,就是 Redis 的「RDB」和「AOF」:

RDB:只长久化某一时刻的数据快照到磁盘上(创立一个子过程来做);

AOF:每一次写操作都长久到磁盘(主线程写内存,依据策略理论状况来决定配置由主线程还是子线程进行数据长久化)。

它们的区别除了下面讲到的,还有以下特点:RDB 采纳二进制 + 数据压缩的形式写磁盘,文件体积小、数据恢复速度也快;AOF 记录的是每一次写命令,数据最全但文件体积大、数据恢复速度慢。

如果让你来抉择长久化计划,你能够这样抉择

● 如果你的业务对于数据失落不敏感,采纳 RDB 计划长久化数据;

● 如果你的业务对数据完整性要求比拟高,采纳 AOF 计划长久化数据。

假如你的业务对 Redis 数据完整性要求比拟高,抉择了 AOF 计划。此时你又会遇到这些问题

● AOF 记录每一次写操作,随着工夫增长,AOF 文件体积会越来越大;

● 微小的 AOF 文件在数据恢复时变得十分慢。

这怎么办?数据完整性要求变高了,复原数据也变艰难了。有没有什么办法,能够放大文件体积、晋升复原速度呢?下文咱们持续来剖析 AOF 的特点。

AOF 文件中记录的是每一次写操作。同一个 key 可能会产生的屡次批改,咱们只保留其最初一次被批改的值,是不是也能够?答案是正确。这就是咱们常常听到的「AOF rewrite」,你也能够把它了解为「AOF 瘦身」。开发者能够对 AOF 文件定时 rewrite,防止这个文件体积继续收缩,以保障在复原时能够缩短复原工夫。

各位开发者们再进一步思考:还有没有方法持续放大 AOF 文件?咱们后面讲到 RDB 和 AOF 各自的特点是 RDB 以二进制 + 数据压缩形式存储,文件体积小;而且 AOF 记录每一次写命令,数据最全。

咱们可否利用它们各自的劣势呢?答案是能够。这就是 Redis 的「混合长久化」。具体来说,当 AOF rewrite 时,Redis 先以 RDB 格局在 AOF 文件中写入一个数据快照,再把在这期间产生的每一个写命令,追加到 AOF 文件中。因为 RDB 是二进制压缩写入的,所以 AOF 文件体积就变得更小了。

此时,你在应用 AOF 文件复原数据时,复原工夫就会更短。Redis 4.0 以上版本才反对混合长久化。通过这么一番优化,你的 Redis 再也不必放心实例宕机了。当产生宕机时,你就能够用长久化文件疾速复原 Redis 中的数据。

但这样就没问题了吗?实际上,尽管咱们曾经把长久化的文件优化到最小,但在复原数据时仍旧是须要工夫的。在这期间你的业务、利用还是会受到影响,怎么办?咱们接下来剖析有没有更好的计划。

一个实例宕机只能用复原数据来解决,那咱们是否能够部署多个 Redis 实例,让这些实例数据放弃实时同步?这样当一个实例宕机时,咱们在剩下的实例中抉择一个持续提供服务就好了。没错,这个计划就是接下来要讲的「主从复制:多正本」。

03、主从复制:多正本

此时,你能够部署多个 Redis 实例,架构模型就变成了这样:

咱们这里把实时读写的节点叫做「master」,另一个实时同步数据的节点叫做「slave」。采纳多正本计划的劣势是:

缩短不可用工夫:master 产生宕机,咱们能够手动把 slave 晋升为 master 持续提供服务;

晋升读性能:让 slave 分担一部分读申请,晋升利用的整体性能。

这个计划不仅节俭了数据恢复的工夫,还能晋升性能。那它有什么问题吗?其实,它的问题在于:当 master 宕机时,咱们须要手动把 slave 晋升为 master,这个过程也是须要破费工夫的。尽管比复原数据要快得多,但还是须要人工染指解决。一旦须要人工染指,就必须要算上人的反应时间、操作工夫。所以,在这期间你的业务、利用依旧会受到影响。

怎么解决这个问题?咱们是否能够把这个切换的过程自动化呢?对于这种状况,咱们须要一个「故障主动切换 」机制——这就是咱们常常听到的「 哨兵」所具备的能力。

04、哨兵:故障主动切换

当初,咱们能够引入一个「观察者 」,让这个观察者去实时监测 master 的衰弱状态。这个观察者就是「 哨兵」。具体如何做:

● 第一,哨兵每距离一段时间,询问 master 是否失常;

● 第二,master 失常回复则示意状态失常,回复超时示意异样;

● 第三,哨兵发现异常,发动主从切换。

有了这个计划,就不须要人去染指解决、所有变得自动化。但这里还有一个问题:如果 master 状态失常,但这个哨兵在询问 master 时,它们之间的网络产生了问题,那这个哨兵可能会误判。

这个问题怎么解决?答案是:咱们能够部署多个哨兵,让它们散布在不同的机器上、一起监测 master 的状态。流程如下:

● 首先,多个哨兵每距离一段时间,询问 master 是否失常;

● 其次,master 失常回复,示意状态失常,回复超时示意异样;

● 再次,一旦有一个哨兵断定 master 异样(不论是否是网络问题),就询问其它哨兵,如果多个哨兵(设置一个阈值)都认为 master 异样了,这才断定 master 的确产生了故障;

● 最初,多个哨兵通过协商后,断定 master 故障,则发动主从切换。

所以,咱们用多个哨兵相互协商来断定 master 的状态。这样一来,就能够大大降低误判的概率。哨兵协商断定 master 异样后,还有一个问题:由哪个哨兵来发动主从切换呢?答案是,选出一个哨兵「领导者」,由这个领导者进行主从切换。

那这个领导者怎么选?设想一下,现实生活中选举是怎么做的?投票。在选举哨兵领导者时,咱们能够制订一个 选举规定

● 每个哨兵都询问其它哨兵,申请对方为本人投票;

● 每个哨兵只投票给第一个申请投票的哨兵,且只能投票一次;

● 首先拿到超过半数投票的哨兵入选为领导者,发动主从切换。

其实,这个选举的过程就是咱们常常听到的:分布式系统畛域中的「共识算法 」。什么是共识算法?咱们在多个机器部署哨兵,它们须要独特合作实现一项工作。所以它们就组成了一个「 分布式系统」。在分布式系统畛域,多个节点如何就一个问题达成共识的算法,就叫共识算法。

在这个场景下,多个哨兵独特协商、选举出一个都认可的领导者,就是应用共识算法实现的。这个算法规定节点的数量必须是奇数个,保证系统中即便有节点产生了故障事残余超过半数的节点状态失常,从而仍旧能够提供正确的后果。也就是说,这个算法兼容了存在故障节点的状况。

共识算法在分布式系统畛域有很多。例如 Paxos、Raft。哨兵选举领导者这个场景,应用的是「Raft 共识算法」。因为它足够简略且易于实现。当初,咱们用多个哨兵独特监测 Redis 的状态。这样一来就能够防止误判的问题。架构模型变成了这样:

咱们先小结一下:你的 Redis 从最简略的单机版,通过数据长久化、主从多正本、哨兵集群的优化,你的 Redis 不论是性能还是稳定性都越来越高,就算节点产生故障也不必放心。你的 Redis 以这样的架构模式部署,根本能够稳固运行很长时间

随着工夫的倒退,你的业务体量开始迎来了爆炸性增长。此时你的架构模型,还可能承当这么大的流量吗?咱们一起来剖析一下:

稳定性:当 Redis 故障宕机时,咱们有哨兵 + 正本,能够主动实现主从切换;

性能:当读申请量增长,咱们能够再部署多个 slave,实现读写拆散以分担读压力;当写申请量增长,咱们只有一个 master 实例。这个实例达到瓶颈怎么办?

当你的写申请量越来越大时,一个 master 实例可能就无奈承当这么大的写流量了。要想完满解决这个问题,此时你就须要思考应用「分片集群」了。

05、分片集群:横向扩大

什么是「分片集群」?一个实例扛不住写压力,咱们能够部署多个实例。把这些实例依照肯定规定组织起来,把它们当成一个整体来对外提供服务,就能够解决集中写一个实例的瓶颈问题。所以,当初的架构模型就变成了这样:

当初问题又来了:这么多实例如何组织呢?咱们制订规定如下:

● 每个节点各自存储一部分数据,所有节点数据之和才是全量数据;

● 制订一个路由规定,将不同的 key 路由到固定一个实例上进行读写。

分片集群依据路由规定所在位置的不同,还能够分为两大类:「客户端分片 」、「 服务端分片」。

「客户端分片」指的是 key 的路由规定放在客户端来做,就是上面这样:

这个计划的毛病是 客户端须要保护路由规定。也就是说,你须要把路由规定写到你的业务代码中。如何做到不把路由规定耦合在业务代码中呢?你能够这样优化:把这个路由规定封装成一个模块。当须要应用时,集成这个模块。

这就是 Redis Cluster 的采纳的计划。

Redis Cluster 内置了哨兵逻辑,无需再部署哨兵。当你应用 Redis Cluster 时,你的业务、利用须要应用配套的 Redis SDK。这个 SDK 内就集成好了路由规定,不须要你本人编写。

再来看「服务端分片 」。这种计划指的是:路由规定不放在客户端来做,而是在客户端和服务端之间减少一个「 两头代理层」。这个代理就是咱们常常听到的 Proxy。数据的路由规定,就放在这个 Proxy 层来保护。

这样一来,你无需关怀服务端有多少个 Redis 节点,只须要和这个「Proxy」交互即可。Proxy 会把你的申请依据路由规定转发到对应的 Redis 节点上。而且,当集群实例不足以撑持更大的流量申请时,还能够横向扩容、增加新的 Redis 实例晋升性能。这所有对于你的客户端来说都是通明无感知的。

这是业界开源的 Redis 分片集群计划,Twemproxy、Codis 也是采纳这种计划。

分片集群在数据扩容时,还波及到了很多细节,此处不加以赘述。如果你对相干内容感兴趣,欢送留言!至此,应用分片集群后,你能够面对将来更大的流量压力。

06、总结

最初咱们来总结一下,咱们是如何一步步构建一个稳固、高性能的 Redis 集群的。首先,在应用最简略的单机版 Redis 时,咱们发现:当 Redis 故障宕机后,数据无奈复原。因而咱们想到了「数据长久化」——把内存中的数据也长久化到磁盘上一份,Redis 重启后就能够从磁盘上疾速复原数据。

在进行数据长久化时,咱们又面临如何更高效地将数据长久化到磁盘的问题。之后咱们发现 Redis 提供了「RDB」和「AOF」两种计划,别离对应了数据快照和实时的命令记录。当咱们对数据完整性要求不高时,能够抉择 RDB 长久化计划。如果对于数据完整性要求较高,能够抉择 AOF 长久化计划。

然而 AOF 文件体积会随着工夫增长变得越来越大。此时咱们想到的优化计划是:应用 AOF rewrite 的形式对其进行瘦身,减小文件体积 。再起初,咱们发现能够联合 RDB 和 AOF 各自的劣势,在 AOF rewrite 时应用两者联合的「 混合长久化」形式,进一步减小 AOF 文件体积。

之后,咱们发现只管能够通过数据恢复的形式还原数据,复原数据也需破费工夫。这意味着业务、利用还是会受到影响。咱们进一步采纳「多正本」的计划——让多个实例放弃实时同步。当一个实例故障时,能够手动把其它实例晋升上来持续提供服务。

然而手动晋升实例上来也须要人工染指,人工染指操作须要工夫。咱们开始想方法把这个流程变得自动化。所以咱们又引入了「哨兵」集群——哨兵集群通过相互协商的形式来发现故障节点,并能够主动实现切换。这大幅升高了对业务、利用的影响。

最初,咱们把关注点聚焦在如何撑持更大的写流量上。咱们又引入了「分片集群」来解决这个问题——多个 Redis 实例摊派写压力,从而面对将来更大的流量。咱们还能够增加新的实例、横向扩大,进一步晋升集群的性能。

至此,咱们的 Redis 集群才得以长期稳固、高性能地为开发者的业务提供服务。最初我整顿了一个思维导图,不便各位开发者更好地去了解它们之间的关系以及演变的过程。以上便是本次分享的全部内容,欢送各位开发者在评论区交换探讨。

-End-

原创作者|谭帅

技术责编|谭帅

你可能感兴趣的腾讯工程师作品

| 腾讯工程师解读 ChatGPT 技术「精选系列文集」

| 国民利用 QQ 如何实现高可用的订阅推送零碎

| 腾讯云开发者热门技术干货汇总

| 7 天 DAU 超亿级,《羊了个羊》技术架构降级实战

技术盲盒:前端|后端|AI 与算法|运维|工程师文化

正文完
 0