最全的 Redis 集群方案

由于 Redis 出众的性能,其在众多的移动互联网企业中得到广泛的应用。Redis 在 3.0 版本前只支持单实例模式,虽然现在的服务器内存可以到 100GB、200GB 的规模,但是单实例模式限制了 Redis 没法满足业务的需求(例如新浪微博就曾经用 Redis 存储了超过 1TB 的数据)。Redis 的开发者 Antirez 早在博客上就提出在 Redis 3.0 版本中加入集群的功能,但 3.0 版本等到 2015 年才发布正式版。各大企业在 3.0 版本还没发布前为了解决 Redis 的存储瓶颈,纷纷推出了各自的 Redis 集群方案。这些方案的核心思想是把数据分片(sharding)存储在多个 Redis 实例中,每一片就是一个 Redis 实例。

  下面介绍 Redis 的集群方案。

  1.客户端分片

  客户端分片是把分片的逻辑放在 Redis 客户端实现,通过 Redis 客户端预先定义好的路由规则,把对 Key 的访问转发到不同的 Redis 实例中,最后把返回结果汇集。这种方案的模式如图 1 所示。

  

图 1 客户端分片的模式

  客户端分片的好处是所有的逻辑都是可控的,不依赖于第三方分布式中间件。开发人员清楚怎么实现分片、路由的规则,不用担心踩坑。

  客户端分片方案有下面这些缺点。

  • 这是一种静态的分片方案,需要增加或者减少 Redis 实例的数量,需要手工调整分片的程序。

  • 可运维性差,集群的数据出了任何问题都需要运维人员和开发人员一起合作,减缓了解决问题的速度,增加了跨部门沟通的成本。

  • 在不同的客户端程序中,维护相同的分片逻辑成本巨大。例如,系统中有两套业务系统共用一套 Redis 集群,一套业务系统用 Java 实现,另一套业务系统用 PHP 实现。为了保证分片逻辑的一致性,在 Java 客户端中实现的分片逻辑也需要在 PHP 客户端实现一次。相同的逻辑在不同的系统中分别实现,这种设计本来就非常糟糕,而且需要耗费巨大的开发成本保证两套业务系统分片逻辑的一致性。

  2.Twemproxy

  Twemproxy 是由 Twitter 开源的 Redis 代理,其基本原理是:Redis 客户端把请求发送到 Twemproxy,Twemproxy 根据路由规则发送到正确的 Redis 实例,最后 Twemproxy 把结果汇集返回给客户端。

  Twemproxy 通过引入一个代理层,将多个 Redis 实例进行统一管理,使 Redis 客户端只需要在 Twemproxy 上进行操作,而不需要关心后面有多少个 Redis 实例,从而实现了 Redis 集群。

  Twemproxy 集群架构如图 2 所示。

  

图 2Twemproxy 集群架构

  Twemproxy 的优点如下。

  • 客户端像连接 Redis 实例一样连接 Twemproxy,不需要改任何的代码逻辑。

  • 支持无效 Redis 实例的自动删除。

  • Twemproxy 与 Redis 实例保持连接,减少了客户端与 Redis 实例的连接数。

  Twemproxy 有如下不足。

  • 由于 Redis 客户端的每个请求都经过 Twemproxy 代理才能到达 Redis 服务器,这个过程中会产生性能损失。

  • 没有友好的监控管理后台界面,不利于运维监控。

  • 最大的问题是 Twemproxy 无法平滑地增加 Redis 实例。对于运维人员来说,当因为业务需要增加 Redis 实例时工作量非常大。

  Twemproxy 作为最被广泛使用、最久经考验、稳定性最高的 Redis 代理,在业界被广泛使用。

  3.Codis

  Twemproxy 不能平滑增加 Redis 实例的问题带来了很大的不便,于是豌豆荚自主研发了 Codis,一个支持平滑增加 Redis 实例的 Redis 代理软件,其基于 Go 和 C 语言开发,并于 2014 年 11 月在 GitHub 上开源。

  Codis 包含下面 4 个部分。

  • Codis Proxy:Redis 客户端连接到 Redis 实例的代理,实现了 Redis 的协议,Redis 客户端连接到 Codis Proxy 进行各种操作。Codis Proxy 是无状态的,可以用 Keepalived 等负载均衡软件部署多个 Codis Proxy 实现高可用。

  • CodisRedis:Codis 项目维护的 Redis 分支,添加了 slot 和原子的数据迁移命令。Codis 上层的 Codis Proxy 和 Codisconfig 只有与这个版本的 Redis 通信才能正常运行。

  • Codisconfig:Codis 管理工具。可以执行添加删除 CodisRedis 节点、添加删除 Codis Proxy、数据迁移等操作。另外,Codisconfig 自带了 HTTP server,里面集成了一个管理界面,方便运维人员观察 Codis 集群的状态和进行相关的操作,极大提高了运维的方便性,弥补了 Twemproxy 的缺点。

  • ZooKeeper:分布式的、开源的应用程序协调服务,是 Hadoop 和 Hbase 的重要组件,其为分布式应用提供一致性服务,提供的功能包括:配置维护、名字服务、分布式同步、组服务等。Codis 依赖于 ZooKeeper 存储数据路由表的信息和 Codis Proxy 节点的元信息。另外,Codisconfig 发起的命令都会通过 ZooKeeper 同步到 CodisProxy 的节点。

  Codis 的架构如图 3 所示。

  

图 3Codis 的架构图

  在图 3 的 Codis 的架构图中,Codis 引入了 Redis Server Group,其通过指定一个主 CodisRedis 和一个或多个从 CodisRedis,实现了 Redis 集群的高可用。当一个主 CodisRedis 挂掉时,Codis 不会自动把一个从 CodisRedis 提升为主 CodisRedis,这涉及数据的一致性问题(Redis 本身的数据同步是采用主从异步复制,当数据在主 CodisRedis 写入成功时,从 CodisRedis 是否已读入这个数据是没法保证的),需要管理员在管理界面上手动把从 CodisRedis 提升为主 CodisRedis。

  如果觉得麻烦,豌豆荚也提供了一个工具 Codis-ha,这个工具会在检测到主 CodisRedis 挂掉的时候将其下线并提升一个从 CodisRedis 为主 CodisRedis。

  Codis 中采用预分片的形式,启动的时候就创建了 1024 个 slot,1 个 slot 相当于 1 个箱子,每个箱子有固定的编号,范围是 1~1024。slot 这个箱子用作存放 Key,至于 Key 存放到哪个箱子,可以通过算法“crc32(key)%1024”获得一个数字,这个数字的范围一定是 1~1024 之间,Key 就放到这个数字对应的 slot。例如,如果某个 Key 通过算法“crc32(key)%1024”得到的数字是 5,就放到编码为 5 的 slot(箱子)。1 个 slot 只能放 1 个 Redis Server Group,不能把 1 个 slot 放到多个 Redis Server Group 中。1 个 Redis Server Group 最少可以存放 1 个 slot,最大可以存放 1024 个 slot。因此,Codis 中最多可以指定 1024 个 Redis Server Group。

  Codis 最大的优势在于支持平滑增加(减少)Redis Server Group(Redis 实例),能安全、透明地迁移数据,这也是 Codis 有别于 Twemproxy 等静态分布式 Redis 解决方案的地方。Codis 增加了 Redis Server Group 后,就牵涉到 slot 的迁移问题。例如,系统有两个 Redis Server Group,Redis Server Group 和 slot 的对应关系如下。

3a4c533d897d4445a8158b4931bca6bc_image.png

  当增加了一个 Redis Server Group,slot 就要重新分配了。Codis 分配 slot 有两种方法。

  第一种:通过 Codis 管理工具 Codisconfig 手动重新分配,指定每个 Redis Server Group 所对应的 slot 的范围,例如可以指定 Redis Server Group 和 slot 的新的对应关系如下。

c66d22e8816b48e8bef7c87471b91330_image.png

  第二种:通过 Codis 管理工具 Codisconfig 的 rebalance 功能,会自动根据每个 Redis Server Group 的内存对 slot 进行迁移,以实现数据的均衡。

  4.Redis 3.0 集群

  Redis 3.0 集群采用了 P2P 的模式,完全去中心化。Redis 把所有的 Key 分成了 16384 个 slot,每个 Redis 实例负责其中一部分 slot。集群中的所有信息(节点、端口、slot 等),都通过节点之间定期的数据交换而更新。

  Redis 客户端在任意一个 Redis 实例发出请求,如果所需数据不在该实例中,通过重定向命令引导客户端访问所需的实例。

  Redis 3.0 集群的工作流程如图 4 所示。

  

图 4Redis 3.0 集群的工作流程图

  如图 4 所示 Redis 集群内的机器定期交换数据,工作流程如下。

  (1) Redis 客户端在 Redis2 实例上访问某个数据。

  (2) 在 Redis2 内发现这个数据是在 Redis3 这个实例中,给 Redis 客户端发送一个重定向的命令。

  (3) Redis 客户端收到重定向命令后,访问 Redis3 实例获取所需的数据。

  Redis 3.0 的集群方案有以下两个问题。

  • 一个 Redis 实例具备了“数据存储”和“路由重定向”,完全去中心化的设计。这带来的好处是部署非常简单,直接部署 Redis 就行,不像 Codis 有那么多的组件和依赖。但带来的问题是很难对业务进行无痛的升级,如果哪天 Redis 集群出了什么严重的 Bug,就只能回滚整个 Redis 集群。

  • 对协议进行了较大的修改,对应的 Redis 客户端也需要升级。升级 Redis 客户端后谁能确保没有 Bug?而且对于线上已经大规模运行的业务,升级代码中的 Redis 客户端也是一个很麻烦的事情。

  综合上面所述的两个问题,Redis 3.0 集群在业界并没有被大规模使用。

  5.云服务器上的集群服务

  国内的云服务器提供商阿里云、UCloud 等均推出了基于 Redis 的云存储服务。

  这个服务的特性如下。

  (1)动态扩容

  用户可以通过控制面板升级所需的 Redis 存储空间,扩容的过程中服务部不需要中断或停止,整个扩容过程对用户透明、无感知,这点是非常实用的,在前面介绍的方案中,解决 Redis 平滑扩容是个很烦琐的任务,现在按几下鼠标就能搞定,大大减少了运维的负担。

  (2)数据多备

  数据保存在一主一备两台机器中,其中一台机器宕机了,数据还在另外一台机器上有备份。

  (3)自动容灾

  主机宕机后系统能自动检测并切换到备机上,实现服务的高可用。

  (4)实惠

  很多情况下为了使 Redis 的性能更高,需要购买一台专门的服务器用于 Redis 的存储服务,但这样子 CPU、内存等资源就浪费了,购买 Redis 云存储服务就很好地解决了这个问题。

  有了 Redis 云存储服务,能使 App 后台开发人员从烦琐运维中解放出来。App 后台要搭建一个高可用、高性能的 Redis 服务,需要投入相当的运维成本和精力。如果使用云存储服务,就没必要投入这些成本和精力,可以让 App 后台开发人员更专注于业务。

转载:https://blog.csdn.net/sanpo/article/details/52839044