在服务开发中,单台机器会出现单点故障,即服务部署在一台服务器上,一旦服务器宕机,服务将无法使用。因此,为了使服务高度可用,会出现分布式服务,同一服务会部署在多台机器上,即使几台服务器宕机,只要有一台服务器可用,该服务就可用。
Redis也是如此。为了解决单机故障,引入了主从模式,但主从模式存在一个问题:主节点故障后的服务需要手动切换到主节点才能恢复服务。为了解决这个问题,Redis引入了sentinel模式,可以在主节点出现故障后,自动将从节点升级为主节点,恢复服务可用性,无需人工干预。但是主从模式和哨兵模式都无法实现真正的数据分片存储,每个Redis实例都存储满数据,于是Redis集群诞生了,实现了真正的数据分片存储。但由于Redis集群发布较晚(官方版本2015年才发布),各大厂商迫不及待,纷纷开发了自己的Redis数据分片集群模型,如Twemproxy、Codis等。
虽然Redis单节点可以通过RDB和AOF持久化机制将数据持久化到硬盘上,但是数据是存储在一台服务器上的。如果服务器出现硬盘故障等问题,会导致数据不可用,读写无法分离。读写都在同一个服务器上,请求量大时会出现I/O瓶颈。
为了避免单点故障和不可分割的读写,Redis提供了复制功能。更新主数据库中的数据后,它会自动将更新后的数据同步到其他从数据库。
Redis有一个主人和许多追随者
Redis的主从结构特点如上:一个主节点可以有多个从节点;从节点可以有从节点,并且从节点是级联的。
「优点:」主从结构具有读写分离、提高效率、数据备份和提供多份拷贝的优点。
「不足:」最大的缺点是主从模式没有自动容错和恢复功能。如果主节点出现故障,集群无法工作,可用性相对较低。从主节点到主节点的升级需要手动干预。
普通主从模式,当主数据库崩溃时,需要手动从从数据库切换到主数据库:
在从属数据库中使用从属NO ONE命令将使从属数据库升级为主数据并继续服务。
启动之前崩溃的主数据库,然后使用slave命令将其设置为新主数据库的从数据库来同步数据。
在主从同步/复制的第一种模式中,当主服务器宕机时,需要手动将一台从服务器切换到主服务器,这需要手动干预,费力且会导致服务在一段时间内不可用,因此需要哨兵模式。Sentinel模式是从Redis 2.6版本提供的,但是当时不稳定,直到Redis 2.8版本才稳定。
哨兵模式的核心是主从复制,但与主从模式相比,当主节点写入失败时,还有一个额外的* *活动机制:*从所有从节点中为新的主节点发起* *活动。活动机制的实现依赖于在系统中启动一个监控进程。
Redis哨兵模式
如上图所示,哨兵本身就存在单点故障的问题,所以在一个一主多从的Redis系统中,可以使用多个哨兵进行监控。哨兵不仅会监视主数据库和从数据库,还会互相监视。每个哨兵都是一个独立的过程。作为一个过程,它将独立运行。
多哨相互监控
监控所有服务器是否正常运行:通过发送命令返回监控服务器的运行状态,监控主服务器、从服务器和哨兵。
故障转移:当哨兵检测到主服务器关闭时,它会自动将从服务器切换到主服务器,然后通过「发布订阅模式」通知其他从服务器修改配置文件,以便
它们切换 master。同时那台有问题的旧主也会变为新主的从,也就是说当旧的主即使恢复时,并不会恢复原来的主身份,而是作为新主的一个从。哨兵在启动进程时,会读取配置文件的内容,通过如下的配置找出需要监控的主数据库:
sentinel monitor master-name ip port quorum
# master-name 是主数据库的名字
# ip 和 port 是当前主数据库地址和端口号
# quorum 表示在执行故障切换操作前,需要多少哨兵节点同意。
这里之所以只需要连接主节点,是因为通过主节点的 info 命令,获取从节点信息,从而和从节点也建立连接,同时也能通过主节点的 info 信息知道新增从节点的信息。
一个哨兵节点可以监控多个主节点,但是并不提倡这么做,因为当哨兵节点崩溃时,同时有多个集群切换会发生故障。哨兵启动后,会与主数据库建立两条连接。
跟主数据库建立连接后会定时执行以下三个操作:
哨兵节点发送 ping 命令时,当超过一定时间(down-after-millisecond)后,如果节点未回复,则哨兵认为主观下线。主观下线表示当前哨兵认为该节点已经下面,如果该节点为主数据库,哨兵会进一步判断是否需要对其进行故障切换,这时候就要发送命令(SENTINEL is-master-down-by-addr)询问其他哨兵节点是否认为该主节点是主观下线,当达到指定数量(quorum)时,哨兵就会认为是客观下线。
当主节点客观下线时就需要进行主从切换,主从切换的步骤为:
选出一个从数据库后,哨兵发送 slave no one 命令升级为主数据库,并发送slaveof 命令将其他从节点的主数据库设置为新的主数据库。
主从模式或哨兵模式每个节点存储的数据都是全量的数据,数据量过大时,就需要对存储的数据进行分片后存储到多个 Redis 实例上。此时就要用到 Redis Sharding 技术。
Redis 在 3.0 版本前只支持单实例模式,虽然 Redis 的开发者 Antirez 早在博客上就提出在 Redis 3.0 版本中加入集群的功能,但 3.0 版本等到 2015 年才发布正式版。各大企业等不及了,在 3.0 版本还没发布前为了解决 Redis 的存储瓶颈,纷纷推出了各自的 Redis 集群方案。这些方案的核心思想是把数据分片(sharding)存储在多个 Redis 实例中,每一片就是一个 Redis 实例。
客户端分片是把分片的逻辑放在 Redis 客户端实现,(比如:jedis 已支持 Redis Sharding 功能,即 ShardedJedis),通过 Redis 客户端预先定义好的路由规则(使用一致性哈希),把对 Key 的访问转发到不同的 Redis 实例中,查询数据时把返回结果汇集。这种方案的模式如图所示。
ShardedJedis分片方案
客户端分片的优缺点:
客户端分片有一个最大的问题就是,服务端 Redis 实例群拓扑结构有变化时,每个客户端都需要更新调整。如果能把客户端分片模块单独拎出来,形成一个单独的模块(中间件),作为客户端 和 服务端连接的桥梁就能解决这个问题了,此时「代理分片」就出现了。
Redis 代理分片用得最多的就是 Twemproxy,由 Twitter 开源的 Redis 代理,其基本原理是:通过中间件的形式,Redis 客户端把请求发送到 Twemproxy,Twemproxy 根据路由规则发送到正确的 Redis 实例,最后 Twemproxy 把结果汇集返回给客户端。
Twemproxy 通过引入一个代理层,将多个 Redis 实例进行统一管理,使 Redis 客户端只需要在 Twemproxy 上进行操作,而不需要关心后面有多少个 Redis 实例,从而实现了 Redis 集群。
Redis代理分片
「Twemproxy 的优点:」
「Twemproxy 的不足:」
「Twemproxy 作为最被广泛使用、最久经考验、稳定性最高的 Redis 代理,在业界被广泛使用。」
Twemproxy 不能平滑增加 Redis 实例的问题带来了很大的不便,于是豌豆荚自主研发了 Codis,一个支持平滑增加 Redis 实例的 Redis 代理软件,其基于 Go 和 C 语言开发,并于 2014 年 11 月在 GitHub 上开源:https://github.com/CodisLabs/codis。
Codis Proxy高可用集群
在 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 的对应关系如下。
当增加了一个 Redis Server Group,slot 就要重新分配了。Codis 分配 slot 有两种方法:
Redis 的哨兵模式虽然已经可以实现高可用,读写分离 ,但是存在几个方面的不足:
Redis 在 3.0 上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储不同的数据。cluster 模式为了解决单机 Redis 容量有限的问题,将数据按一定的规则分配到多台机器,内存/QPS 不受限于单机,可受益于分布式集群高扩展性。Redis Cluster 是一种服务器 Sharding 技术(分片和路由都是在服务端实现),「采用多主多从,每一个分区都是由一个 Redis 主机和多个从机组成,片区和片区之间是相互平行的」。Redis Cluster 集群采用了 P2P 的模式,完全去中心化。
Redis Cluster 集群模式
如上图,官方推荐,集群部署至少要 3 台以上的 master 节点,最好使用 3 主 3 从六个节点的模式。Redis Cluster 集群具有如下几个特点:
「Redis cluster 主要是针对海量数据 + 高并发 + 高可用的场景,海量数据,如果你的数据量很大,那么建议就用 Redis cluster,数据量不是很大时,使用 sentinel 就够了。Redis cluster 的性能和高可用性均优于哨兵模式。」
Redis Cluster 采用虚拟哈希槽分区而非一致性 hash 算法,预先分配一些卡槽,所有的键根据哈希函数映射到这些槽内,每一个分区内的 master 节点负责维护一部分槽以及槽所映射的键值数据。