2026/4/6 7:51:02
网站建设
项目流程
电子商务网站基本功能,wordpress 网页抓取,深圳app推广平台,深色网站这篇文章我想和你聊一聊 Redis 的架构演化之路。现如今 Redis 变得越来越流行#xff0c;几乎在很多项目中都要被用到#xff0c;不知道你在使用 Redis 时#xff0c;有没有思考过#xff0c;Redis 到底是如何稳定、高性能地提供服务的#xff1f;我使用 Redis 的场景很简…这篇文章我想和你聊一聊 Redis 的架构演化之路。现如今 Redis 变得越来越流行几乎在很多项目中都要被用到不知道你在使用 Redis 时有没有思考过Redis 到底是如何稳定、高性能地提供服务的我使用 Redis 的场景很简单只使用单机版 Redis 会有什么问题吗我的 Redis 故障宕机了数据丢失了怎么办如何能保证我的业务应用不受影响为什么需要主从集群它有什么优势什么是分片集群我真的需要分片集群吗...如果你对 Redis 已经有些了解肯定也听说过「数据持久化、主从复制、哨兵、分片集群」这些概念它们之间又有什么区别和联系呢如果你存在这样的疑惑这篇文章我会从 0 到 1再从 1 到 N带你一步步构建出一个稳定、高性能的 Redis 集群。在这个过程中你可以了解到 Redis 为了做到稳定、高性能都采取了哪些优化方案以及为什么要这么做掌握了这些原理这样平时你在使用 Redis 时就能够做到「游刃有余」。从最简单的开始单机版 Redis首先我们从最简单的场景开始。假设现在你有一个业务应用需要引入 Redis 来提高应用的性能此时你可以选择部署一个单机版的 Redis 来使用就像这样这个架构非常简单你的业务应用可以把 Redis 当做缓存来使用从 MySQL 中查询数据然后写入到 Redis 中之后业务应用再从 Redis 中读取这些数据由于 Redis 的数据都存储在内存中所以这个速度飞快。如果你的业务体量并不大那这样的架构模型基本可以满足你的需求。是不是很简单随着时间的推移你的业务体量逐渐发展起来了Redis 中存储的数据也越来越多此时你的业务应用对 Redis 的依赖也越来越重。突然有一天你的 Redis 因为某些原因宕机了这时你的所有业务流量都会打到后端 MySQL 上MySQL 压力剧增严重的话甚至会压垮 MySQL。这时你应该怎么办我猜你的方案肯定是赶紧重启 Redis让它可以继续提供服务。但是因为之前 Redis 中的数据都在内存中尽管你现在把 Redis 重启了之前的数据也都丢失了假设没开持久化。重启后的 Redis 虽然可以正常工作但是由于 Redis 中没有任何数据业务流量还是都会打到后端 MySQL 上MySQL 的压力还是很大。有没有什么好的办法解决这个问题既然 Redis 只把数据存储在内存中那是否可以把这些数据也写一份到磁盘上呢如果采用这种方式当 Redis 重启时我们把磁盘中的数据快速「恢复」到内存中这样它就可以继续正常提供服务了。是的这是一个很好的解决方案这个把内存数据写到磁盘上的过程就是「数据持久化」。数据持久化有备无患现在你设想的 Redis 数据持久化是这样的3但是数据持久化具体应该怎么做呢我猜你最容易想到的一个方案是Redis 每一次执行写操作除了写内存之外同时也写一份到磁盘上就像这样没错这是最简单直接的方案。但仔细想一下这个方案有个问题客户端的每次写操作既需要写内存又需要写磁盘而写磁盘的耗时相比于写内存来说肯定要慢很多这势必会影响到 Redis 的性能。如何规避这个问题这时我们需要分析写磁盘的细节问题了。我们都知道要把内存数据写到磁盘其实是分 2 步的程序写文件的 PageCachewrite把 PageCache 刷到磁盘fsync具体就是下图这样数据持久化最粗暴的思路就是上面提到的那样写完 Redis 内存后同步写 PageCache fsync 磁盘当然这样肯定因为磁盘拖慢整个写入速度。如何优化也很简单我们可以这样做Redis 写内存由主线程来做写内存完成后就给客户端返回结果然后 Redis 用「另一个线程」去写磁盘这样就可以避免主线程写磁盘对性能的影响。这种持久化方案其实就是我们经常听到的 Redis AOFAppend Only File。Redis AOF 持久化提供了 3 种刷盘机制appendfsync always主线程同步 fsyncappendfsync no由 OS fsyncappendfsync everysec后台线程每间隔1秒 fsync解决了数据实时持久化我们还会面临另一个问题数据实时写入 AOF随着时间的推移AOF 文件会越来越大那使用 AOF 恢复时变得非常慢这该怎么办Redis 很贴心地提供了 AOF rewrite 方案俗称 AOF 「瘦身」顾名思义就是压缩 AOF 的体积。因为 AOF 里记录的都是每一次写命令例如执行 set k1 v1set k1 v2其实我们只关心数据的最终版本 v2 就可以了。AOF rewrite 正是利用了这个特点在 AOF 体积越来越大时超过设定阈值Redis 就会定期重写一份新的 AOF这个新的 AOF 只记录数据的最终版本就可以了。这样就可以压缩 AOF 体积。除此之外我们可以换个角度思考一下还有什么方式可以持久化数据这时你就要结合 Redis 的使用场景来考虑了。回忆一下我们在使用 Redis 时通常把它用作什么场景是的缓存。把 Redis 当做缓存来用意味着尽管 Redis 中没有保存全量数据对于不在缓存中的数据我们的业务应用依旧可以通过查询后端数据库得到结果只不过查询后端数据的速度会慢一点而已但对业务结果其实是没有影响的。基于这个特点我们的 Redis 数据持久化还可以用「数据快照」的方式来做。那什么是数据快照呢简单来讲你可以这么理解你把 Redis 想象成一个水杯向 Redis 写入数据就相当于往这个杯子里倒水此时你拿一个相机给这个水杯拍一张照片拍照的这一瞬间照片中记录到这个水杯中水的容量就是水杯的数据快照也就是说Redis 的数据快照是记录某一时刻下 Redis 中的数据然后只需要把这个数据快照写到磁盘上就可以了。它的优势在于只在需要持久化时把数据「一次性」写入磁盘其它时间都不需要操作磁盘。基于这个方案我们可以「定时」给 Redis 做数据快照把数据持久化到磁盘上。这种方案就是我们经常听到的 Redis RDBRDB 采用「定时快照」的方式进行数据持久化它的优点是持久化文件体积小二进制 压缩写盘频率低定时写入缺点也很明显因为是定时持久化数据肯定没有 AOF 实时持久化完整如果你的 Redis 只当做缓存对于丢失数据不敏感可从后端的数据库查询那这种持久化方式是非常合适的。如果让你来选择持久化方案你可以这样选择业务对于数据丢失不敏感选 RDB业务对数据完整性要求比较高选 AOF理解了 RDB 和 AOF我们再进一步思考一下有没有什么办法既可以保证数据完整性还能让持久化文件体积更小恢复更快呢回顾一下我们前面讲到的RDB 和 AOF 各自的特点RDB 以二进制 数据压缩方式存储文件体积小AOF 记录每一次写命令数据最全我们可否利用它们各自的优势呢当然可以这就是 Redis 的「混合持久化」。要想数据完整性更高肯定就不能只用 RDB 了重点还是要放在 AOF 优化上。具体来说当 AOF 在做 rewrite 时Redis 先以 RDB 格式在 AOF 文件中写入一个数据快照再把在这期间产生的每一个写命令追加到 AOF 文件中。因为 RDB 是二进制压缩写入的这样 AOF 文件体积就变得更小了。因为 AOF 体积进一步压缩你在使用 AOF 恢复数据时这个恢复时间就会更短了Redis 4.0 以上版本才支持混合持久化。注意混合持久化是对 AOF rewrite 的优化这意味着使用它必须基于 AOF AOF rewrite。这么一番优化你的 Redis 再也不用担心实例宕机了当发生宕机时你就可以用持久化文件快速恢复 Redis 中的数据。但这样就没问题了吗仔细想一下虽然我们已经把持久化的文件优化到最小了但在恢复数据时依旧是需要时间的在这期间你的业务应用无法提供服务这怎么办一个实例宕机只能用恢复数据来解决那我们是否可以部署多个 Redis 实例然后让这些实例数据保持实时同步这样当一个实例宕机时我们在剩下的实例中选择一个继续提供服务就好了。没错这个方案就是接下来要讲的「主从复制多副本」。主从复制多副本你可以部署多个 Redis 实例架构模型就变成了这样我们这里把实时读写的节点叫做 master另一个实时同步数据的节点叫做 slave。采用多副本的方案它的优势是缩短不可用时间master 发生宕机我们可以手动把 slave 提升为 master 继续提供服务提升读性能让 slave 分担一部分读请求提升应用的整体性能这个方案不错不仅节省了数据恢复的时间还能提升性能。但它的问题在于当 master 宕机时我们需要「手动」把 slave 提升为 master这个过程也是需要花费时间的。虽然比恢复数据要快得多但还是需要人工介入处理。一旦需要人工介入就必须要算上人的反应时间、操作时间所以在这期间你的业务应用依旧会受到影响。我们是否可以把这个切换的过程变成自动化哨兵故障自动切换要想自动切换肯定不能依赖人了。现在我们可以引入一个「观察者」让这个观察者去实时监测 master 的健康状态这个观察者就是「哨兵」。具体如何做哨兵每间隔一段时间询问 master 是否正常master 正常回复表示状态正常回复超时表示异常哨兵发现异常发起主从切换有了这个方案就不需要人去介入处理了一切就变得自动化了是不是很爽但这里还有一个问题如果 master 状态正常但这个哨兵在询问 master 时它们之间的网络发生了问题那这个哨兵可能会「误判」。这个问题怎么解决既然一个哨兵会误判那我们可以部署多个哨兵让它们分布在不同的机器上让它们一起监测 master 的状态流程就变成了这样多个哨兵每间隔一段时间询问 master 是否正常master 正常回复表示状态正常回复超时表示异常一旦有一个哨兵判定 master 异常不管是否是网络问题就询问其它哨兵如果多个哨兵设置一个阈值都认为 master 异常了这才判定 master 确实发生了故障多个哨兵经过协商后判定 master 故障则发起主从切换所以我们用多个哨兵互相协商来判定 master 的状态这样就可以大大降低误判的概率。哨兵协商判定 master 异常后这里还有一个问题由哪个哨兵来发起主从切换呢答案是选出一个哨兵「领导者」由这个领导者进行主从切换。问题又来了这个领导者怎么选想象一下在现实生活中选举是怎么做的是的投票。在选举哨兵领导者时我们可以制定这样一个选举规则每个哨兵都询问其它哨兵请求对方为自己投票每个哨兵只投票给第一个请求投票的哨兵且只能投票一次首先拿到超过半数投票的哨兵当选为领导者发起主从切换这个选举的过程就是我们经常听到的分布式系统领域中的「共识算法」。什么是共识算法我们在多个机器部署哨兵它们需要共同协作完成一项任务所以它们就组成了一个「分布式系统」。在分布式系统领域多个节点如何就一个问题达成共识的算法就叫共识算法。在这个场景下多个哨兵共同协商选举出一个都认可的领导者就是使用共识算法完成的。这个算法还规定节点的数量必须是奇数个这样可以保证系统中即使有节点发生了故障剩余超过「半数」的节点状态正常依旧可以提供正确的结果也就是说这个算法还兼容了存在故障节点的情况。共识算法在分布式系统领域有很多例如 Paxos、Raft哨兵选举领导者这个场景使用的是 Raft 共识算法因为它足够简单且易于实现。好到这里我们先小结一下。你的 Redis 从最简单的单机版经过数据持久化、主从多副本、哨兵集群这一路优化下来你的 Redis 不管是性能还是稳定性都越来越高就算节点发生故障也不用担心了。Redis 以这样的架构模式部署基本上就可以稳定运行很长时间了。...随着时间的发展你的业务体量开始迎来了爆炸性增长此时你的架构模型还能够承担这么大的流量吗我们一起来分析一下数据怕丢失持久化RDB/AOF恢复时间久主从副本副本随时可切手动切换时间长哨兵集群自动切换读存在压力扩容副本读写分离写存在压力一个 mater 扛不住怎么办可见现在剩下的问题是当写请求量越来越大时一个 master 实例可能就无法承担这么大的写流量了。要想完美解决这个问题此时你就需要考虑使用「分片集群」了。分片集群横向扩展什么是「分片集群」简单来讲一个实例扛不住写压力那我们是否可以部署多个实例然后把这些实例按照一定规则组织起来把它们当成一个整体对外提供服务这样不就可以解决集中写一个实例的瓶颈问题吗所以现在的架构模型就变成了这样现在问题又来了这么多实例如何组织呢我们制定规则如下每个节点各自存储一部分数据所有节点数据之和才是全量数据制定一个路由规则对于不同的 key把它路由到固定一个实例上进行读写数据分多个实例存储那寻找 key 的路由规则需要放在客户端来做具体就是下面这样这种方案也叫做「客户端分片」这个方案的缺点是客户端需要维护这个路由规则也就是说你需要把路由规则写到你的业务代码中。如何做到不把路由规则耦合在客户端业务代码中呢继续优化我们可以在客户端和服务端之间增加一个「中间代理层」这个代理就是我们经常听到的 Proxy路由转发规则放在这个 Proxy 层来维护。这样客户端就无需关心服务端有多少个 Redis 节点了只需要和这个 Proxy 交互即可。Proxy 会把你的请求根据路由规则转发到对应的 Redis 节点上而且当集群实例不足以支撑更大的流量请求时还可以横向扩容添加新的 Redis 实例提升性能这一切对于你的客户端来说都是透明无感知的。业界开源的 Redis 分片集群方案例如 Twemproxy、Codis 就是采用的这种方案。这种方案的优点在于客户端无需关心数据转发规则只需要和 Proxy 打交道客户端像操作单机 Redis 那样去操作后面的集群简单易用。架构演进到目前为止路由规则无论是客户端来做还是 Proxy 来做都是「社区」演进出来的分片解决方案它们的特点是集群中的 Redis 节点都不知道对方的存在只有客户端或 Proxy 才会统筹数据写到哪里从哪里读取而且它们都依赖哨兵集群负责故障自动切换。也就是说我们其实就是把多个孤立的 Redis 节点自己组合起来使用。Redis 在 3.0 其实就推出了「官方」的 Redis Cluster 分片方案但由于推出初期不稳定所以用的人很少也因此业界涌现出了各种开源方案上面讲到的 Twemproxy、Codis 分片方案就是在这种背景下诞生的。但随着 Redis Cluster 方案的逐渐成熟业界越来越多的公司开始采用官方方案毕竟官方保证持续维护Twemproxy、Codis 目前都逐渐放弃维护了Redis Cluster 方案比上面讲到的分片方案更简单它的架构如下。Redis Cluster 无需部署哨兵集群集群内 Redis 节点通过 Gossip 协议互相探测健康状态在故障时可发起自动切换。另外关于路由转发规则也不需要客户端自己编写了Redis Cluster 提供了「配套」的 SDK只要客户端升级 SDK就可以和 Redis Cluster 集成SDK 会帮你找到 key 对应的 Redis 节点进行读写还能自动适配 Redis 节点的增加和删除业务侧无感知。虽然省去了哨兵集群的部署维护成本降低了不少但对于客户端升级 SDK对于新业务应用来说可能成本不高但对于老业务来讲「升级成本」还是比较高的这对于切换官方 Redis Cluster 方案有不少阻力。于是各个公司有开始自研针对 Redis Cluster 的 Proxy降低客户端的升级成本架构就变成了这样这样客户端无需做任何变更只需把连接地址切到 Proxy 上即可由 Proxy 负责转发数据以及应对后面集群增删节点带来的路由变更。至此业界主流的 Redis 分片架构已经成型当你使用分片集群后对于未来更大的流量压力也都可以从容面对了总结总结一下我们是如何从 0 到 1再从 1 到 N 构建一个稳定、高性能的 Redis 集群的从这之中你可以清晰地看到 Redis 架构演进的整个过程。数据怕丢失持久化RDB/AOF恢复时间久主从副本副本随时可切故障手动切换慢哨兵集群自动切换读存在压力扩容副本读写分离写存在压力/容量瓶颈分片集群分片集群社区方案Twemproxy、CodisRedis 节点之间无通信需要部署哨兵可横向扩容分片集群官方方案Redis Cluster Redis 节点之间 Gossip 协议无需部署哨兵可横向扩容业务侧升级困难Proxy Redis Cluster不侵入业务侧至此我们的 Redis 集群才得以长期稳定、高性能的为我们的业务提供服务。希望这篇文章可以帮你更好的理解 Redis 架构的演进之路