Redis分布式集群
前言
Redis
在3.0以后的版本支持了Cluster
那么我们首先会想到Cluster
是解决怎样的应用场景
为了应对大流量访问下提供稳定的业务,集群化是存储的必然形态 之前的单点存储势必会有诸多隐患
而未来的发展趋势肯定是大数据和云计算的紧密集合 分布式架构也就可以很好的体现他的优势
分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数来提升效率
解决方案
目前的Redis
的集群化方案有三种
- Twitter开发的twemproxy
- 豌豆荚开发的codis
- redis官方的redis-cluster
redis-cluster
是三个里性能最强大的 因为他使用去中心化的思想 使用hash slot
方式 将16348个hash slot
覆盖到所有节点上
对于存储的每个key
值 使用CRC16(KEY)&16348=slot
得到他对应的hash slot
并在访问key
时就去找他的hash slot
在哪一个节点上
然后由当前访问节点从实际被分配了这个hash slot
的节点去取数据 节点之间使用轻量协议通信 减少带宽占用 性能很高
自动实现负载均衡与高可用 自动实现failover
并且支持动态扩展 官方已经玩到可以1000个节点 实现的复杂度低 因为他的去中心化思想免去了proxy
的消耗 是全新的思路
基本介绍
Redis
集群是一个可以在多个 Redis
节点之间进行数据共享的设施installation
Redis
集群通过分区partition
来提供一定程度的可用性availability
: 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。
Redis
集群提供了以下两个好处:
- 将数据自动切分
split
到多个节点的能力。 - 当集群中的一部分节点失效或者无法进行通讯时, 仍然可以继续处理命令请求的能力。
集群分区
常见的分区规则有哈希分区和顺序分区,redis
集群使用了哈希分区。RedisCluster
采用了哈希分区的'虚拟槽分区'方式(哈希分区节点取余,一致性哈希分区和虚拟槽分区)
RedisCluster
采用嘘嘘你槽分区,所有的键根据哈希函数(CRC16[key]&16383
)映射到0-16383槽内,一共16384个槽位。每个节点维护部分及槽所映射的键值数据
哈希函数: Hash() = CRC16[key]&16383
按位与
其中槽与节点的关系如下:


环境安装
本地安装Redis
服务 我因为是mac
环境 所以直接可以通过homebrew
安装
$ brew install redis
为了搭建 目录这里新建了6个节点目录
将redis
安装包 执行bin
目录分别copy
到每个节点目录
可以看到redis bin
目录下的一些redis
执行命令文件

集群搭建
将本地redis
的redis-trib.rb
复制到redis-cluster
目录
复制redis
的配置文件到每个节点 注意本机的conf
文件的地址为 /usr/local/etc/redis.conf
完毕之后 集群文件的目录是这样的:
接下来就是针对每个节点的配置了 这里我们进入节点目录下的配置文件redis.conf
这里我们需要编辑的配置信息如下:
port 7001 //节点端口
daemonize yes //配置redis作为守护进程运行,默认情况下,redis不是作为守护进程运行的
bind 127.0.0.1 //默认127.0.0.1,需要改为其他节点可访问的地址
cluster-node-timeout 5000 //集群超时时间
cluster-enabled yes //redis 集群
cluster-config-file nodes-7001.conf //指定节点配置信息
appendonly yes //存储方式
dir /Users/gehuachun/Develop/redis-cluster/redis01 //指定本地数据库路径
节点启动
每个节点配置完毕之后开始依次启动每个节点 这里假设启动是06这个节点
启动完毕之后查看redis
的服务我们可以看到
集群创建
节点启动完毕之后开始创建集群 集群文件目录终端执行
$ redis-trib.rb create --replicas 1 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006
其中--replices 1指定了为每个节点分配一个从节点

安装过程如果遇到插件报错 可以尝试:
那么在创建的过程中你将会看到如下信息:
最终集群创建成功时 你会看到
注意看后面的返回信息 这里指示了急群众主从节点的关系信息 比如:
可以看到7005是7001这个节点的从节点 因为这在创建集群的6个节点所生成的主从关系 当然你也可以单独指定主从节点
那么创建集群完毕之后再去查看集群节点目录 你会发现多了日志存储文件
集群连接
既然集群创建爱好了 我们开始尝试连接 这里我们先从7001节点开始并查看集群的信息
$ redis-cli -c -h 127.0.0.1 -p 7001
$ cluster nodes //查看集群信息

连接上7001这个节点 可以测试下与其他节点的通信 即与节点握手
就像之前说的 我们是可以手动建立主从关系的 我们再查看节点信息时 看好节点的id
槽的重新分配
集群创建完后 我们可以对所给我们的hash
进行重新分配 比如:

all 表示节点的槽位全部重新洗牌
分配后的槽 可以看到7003这个节点多了500个槽 也就是 0-248
5461-5711
这之间的500个
集群是为了处理一块业务 比如数据的存储 不过是通过不同的机器
如果没有重新分配槽 那么我这里新起了集群 最终的槽点的分布式这样的 注意主节点分配的信息
集群正常启动后,在每个redis.conf
里加上
masterauth “12345678”
requiredpass “12345678”
当主节点下线时,从节点会变成主节点,用户和密码是很有必要的,设置成一致
集群节点之间的通信
1.节点之间采用Gossip
协议进行通信,Gossip
协议就是指节点彼此之间不断通信交换信息
当主从角色变化或新增节点,彼此通过ping/pong进行通信知道全部节点的最新状态并达到集群同步
- Gossip协议
Gossip
协议的主要职责就是信息交换,信息交换的载体就是节点之间彼此发送的Gossip
消息,常用的Gossip
消息有ping
消息、pong
消息、meet
消息、fail
消息
meet
消息:用于通知新节点加入,消息发送者通知接收者加入到当前集群,meet
消息通信完后,接收节点会加入到集群中,并进行周期性ping
pong
交换
ping
消息:集群内交换最频繁的消息,集群内每个节点每秒向其它节点发ping
消息,用于检测节点是在在线和状态信息,ping
消息发送封装自身节点和其他节点的状态数据;
pong
消息,当接收到ping
meet
消息时,作为响应消息返回给发送方,用来确认正常通信,pong
消息也封闭了自身状态数据;
fail
消息:当节点判定集群内的另一节点下线时,会向集群内广播一个fail
消息,
集群扩容
这也是分布式存储最常见的需求,当我们存储不够用时,要考虑扩容
启动新节点 这里启动7007 和7008以备用
这里新增了两目录redis07
和redis08
复制之前的节点配置文件 需要修改的就是port
和dir
数据存储的路径这两个参数
集群新增节点
./redis-trib.rb add-node
命令中 7007是新增的主节点 7001则是集群中已存在的节点
添加完毕后再查看下集群的节点信息
发现多了7007这个新的主节点
添加从节点 现在将7008添加作为7007的从节点 我们需要7007 的nodeid
可以通过clutser nodes
查看
可以看到7c22cf36b86bc9efcd5d4cd8cab300462090b087 这个是7007的节点id --slave
表示添加从节点
7008为需要加入到集群的从节点 7001为集群中已经存在的节点
现在可以再去查看集群各节点信息
OK
添加完毕
但是还没有结束 新添加的7007节点并没有为他分配槽 所以他还算不上主节点
和上面提过的一样 为7007这个节点重新分配节点 7007的节点id
为7c22cf36b86bc9efcd5d4cd8cab300462090b087
redis-trib.rb reshard 127.0.0.7 //为新主节点重新分配solt
How many slots do you want to move (from 1 to 16384)? 1000 //设置slot数1000
What is the receiving node ID? 7c22cf36b86bc9efcd5d4cd8cab300462090b087 //新节点node id
Source node #1:all //表示全部节点重新洗牌
新增完毕 再次查看集群信息
删除节点
删除已经占有hash
槽的结点会失败 所以删除前需要把槽位分配出去即可
指定节点的id
即可 比如这里删除7008
$ redis-trib.rb del-node 127.0.0.1:7008 2113ab21277cb952d77d269fe69f8a852000d949

查看删除后的集群信息
此时的节点也算是下线完成
故障处理
redis
集群实现了高可用,当集群内少量节点出现故障时,通过故障转移可以保证集群正常对外提供服务。当集群里某个节点出现了问题,redis
集群内的节点通过ping
pong
消息发现节点是否健康,是否有故障,其实主要环节也包括了 主观下线和客观下线;
主观下线:指某个节点认为另一个节点不可用,即下线状态,当然这个状态不是最终的故障判定,只能代表这个节点自身的意见,也有可能存在误判
下线流程:
- 节点
a
发送ping
消息给节点b
,如果通信正常将接收到pong
消息,节点a
更新最近一次与节点b
的通信时间;
2.如果节点a
与节点b
通信出现问题则断开连接,下次会进行重连,如果一直通信失败,则它们的最后通信时间将无法更新;
3.节点a
内的定时任务检测到与节点b
最后通信时间超过cluster_note-timeout
时,更新本地对节点b
的状态为主观下线(pfail)
客观下线:指真正的下线,集群内多个节点都认为该节点不可用,达成共识,将它下线,如果下线的节点为主节点,还要对它进行故障转移
假如节点a
标记节点b
为主观下线,一段时间后节点a
通过消息把节点b的状态发到其它节点,当节点c
接受到消息并解析出消息体时,会发现节点b
的pfail
状态时,会触发客观下线流程
当下线为主节点时,此时redis
集群为统计持有槽的主节点投票数是否达到一半,当下线报告统计数大于一半时,被标记为客观下线状态。
故障恢复
故障主节点下线后,如果下线节点的是主节点,则需要在它的从节点中选一个替换它,保证集群的高可用;转移过程如下:
1.资格检查:检查该从节点是否有资格替换故障主节点,如果此从节点与主节点断开过通信,那么当前从节点不具体故障转移;
2.准备选举时间:当从节点符合故障转移资格后,更新触发故障选举时间,只有到达该时间后才能执行后续流程;
3.发起选举:当到达故障选举时间时,进行选举;
4.选举投票:只有持有槽的主节点才有票,会处理故障选举消息,投票过程其实是一个领导者选举(选举从节点为领导者)的过程,每个主节点只能投一张票给从节点,当从节点收集到足够的选票(大于N/2+1
)后,触发替换主节点操作,撤销原故障主节点的槽,委派给自己,并广播自己的委派消息,通知集群内所有节点。
RedisCluster 缺陷
1.键的批量操作支持有限,比如mset
、mget
,如果多个键映射再不同的槽,就不支持了
2.键的事务支持有限,当多个key
分布在不同的节点时无法使用事务,同一个节点时支持事务的
3.键是数据分区的最小粒度,不能讲一个很大的键值映射到不同的节点
4.不支持多数据库,只有0
,select 0
5.复制结构只支持单层结构,不支持树状结构