为什么要搭建Redis集群?
高可用
单个Redis如果因为某种原因宕机的话,可能会导致Redis服务不可用,可以使用主从复制实现一主多从,主节点负责写的操作,从节点负责读的操作,主节点会定期将数据同步到从节点中,保证数据一致性的问题。
Redis主从复制是指:将一台 Redis 服务器的数据复制到其它的 Redis 服务器,前者所在的 Redis 服务器也被称为 “主节点”(Master / Leader),后者则被称为 “从节点”(Slave / Follower)。数据从主节点复制到从节点,主节点的主要任务是实现写如数据的任务(也有读数据的权限),而从节点则只负责读取数据。在 Redis 的默认配置中,每个启动的 Redis 服务都是主节点。
一个主节点可以有多个从节点,但是对于每个从节点来讲,它都只能属于一个主节点。即主节点和从节点之间的对应关系为 1 对 N ,如下图所示:

该主从同步方式存在 如果从节点非常多的话,会导致对主节点同步多个从节点压力非常大,可以采用树状类型解决该问题。
Redis 的主从复制分为以下两个阶段:sync阶段和 command propagate阶段。
sync(同步)阶段
当从节点启动之后,会发送 sync 指令给主节点,要求全量同步数据,具体的步骤如下图所示:

Slave 节点向 Master 节点发送 sync 指令,以请求数据同步Master 节点在接收到 sync 指令后,会执行一次 BGSAVE 指令,将当前 Master 节点中的数据保存到对应的 RDB 文件中。当 Master 节点完成 RDB 文件的导出后,再将导出的 RBD 文件发送给 Slave 节点。由于在这个过程中 Master 节点依旧有可能发生数据写入操作,在这种情况下 Master 节点会将执行的指令放入到对应的缓冲区Slave 节点在接受到 Master 节点导出的 RDB 文件之后,会删除现有节点的所有数据,然后加载这个 RDB 文件的内容到 Slave 节点Slave 节点数据加载完成之后,Master 会将缓冲区中暂存的指令发送到 Slave 节点Slave 执行收到的指令,完成数据的同步Command Propagate 阶段
这个阶段也被称为 “命令传播” 阶段,数据同步完成之后,如果后续 Master 节点继续收到了新的写入操作的指令,那么也需要将该命令传播到 Slave 节点以完成数据的同步。这个过程就被称为 “命令传播”
从Redis2.8版本以后,过程采用增量和全量同步
全量复制:一般用于在初次的复制场景(从节点与主节点一次建立连接)
增量复制:网络出现问题,从节点再次连接主节点时,主节点补发缺少的数据,每次数据增量同步
如果主节点存在了问题,整个Redis环境是不可以实现写的操作,需要人工更改配置变为主操作
如何解决该问题:使用哨兵机制可以帮助解决Redis集群主从选举策略。
Redis 主节点正常启动。
Redis 从节点配置文件Redis.conf配置:指定主节点的IP、端口号、密码
# replicaof <masterip> <masterport>
slaveof 192.168.212.160 6379
masterauth 123456

为防止主节点同步数据压力过大,可以采用树状模式,也就是从节点作为主节点也可以有从节点,如下图所示:

Redis的哨兵机制就是解决以上主从复制存在缺陷(如果 Master 节点崩溃了,在上面的情况下不会将 Slave 节点转换为 Master 节点,因此 Master 节点崩溃之后整个 Redis 集群就不能再执行写入操作),解决问题保证Redis高可用,提高系统的可用性,Redis 提供了 Sentinel(哨兵)来实现 Slave 节点到 Master 节点的转换,“哨兵” 节点本质上也是一个 Redis 节点,但是和 Master 节点和 Slave 节点不同,“哨兵” 节点只是监视 Master 节点和 Slave 节点,并不执行相关的业务操作。

哨兵作用:
由于存在多个 哨兵 节点,因此在 Redis Sentinel 中,对于 Redis 节点的下线也有区分:
一个 Sentinel 节点可以通过向另一个 Sentinel 节点发送 SENTINEL is-master-down-by-addr 命令来询问对方是否认为给定的节点已经下线
选举过程:
在主从复制的基础上,启动哨兵服务,多台redis的哨兵监听同一台master节点,哨兵会自动形成一个哨兵集群。
配置哨兵的配置文件:
sentinel monitor mymaster 192.168.212.160 6379 3(3表示主节点宕机之后,3台哨兵确认之后,才能重新进行选举)
sentinel auth-pass mymaster 123456 #主节点的密码
sentinel down-after-milliseconds mymaster 3000#sentinel心跳检测主3秒内无响应,视为挂掉,开始切换其他从为主
sentinel parallel-syncs mymaster 1 #每次最多可以有1个从同步主。一个从同步结束,另一个从开始同步。
sentinel failover-timeout mymaster 18000 #主从切换超时时间
启动哨兵:到 redis bin 目录下
./redis-sentinel ./sentinel.conf
Redis3.0开始官方推出了集群模式 RedisCluster,原理:采用hash槽的概念,预先分配16384个卡槽,并且将该卡槽分配给具体服务的节点;通过key进行crc16(key)%16384 获取余数,余数就是对应的卡槽的位置,一个卡槽可以存放多个不同的key,从而将读或者写转发到该卡槽的服务的节点。 最大的优点:动态扩容、缩容。
只有主的节点才有卡槽,从节点没有卡槽。
卡槽作用:决定key存在具体服务器的位置,从而实现均摊存入数据。类似于我们在数据库中的分表。
这里在一台机器上面搭建
1、创建留个文件夹
mkdir rediscluster
cd rediscluster/
mkdir redis7000
mkdir redis7001
mkdir redis7002
mkdir redis7003
mkdir redis7004
mkdir redis7005
2、每个文件夹中编写一个配置文件,内容如下:
注意:注释要单独放一行、data目录需要先创建好
#后台启动
daemonize yes
## 允许外部访问
protected-mode no
#修改端口号,从7000到7005
port 7005
#开启cluster(开启集群,redis默认支持集群),去掉注释
cluster-enabled yes
#自动生成
cluster-config-file 7000nodes.conf
#节点通信时间
cluster-node-timeout 15000
#对应的7005根据每个端口号修改
logfile /opt/redis/redisCluster/redis7005/redis.log
#修改数据文件存储位置
dir /opt/redis/redisCluster/redis7002/data/
3、一次启动6个接口的redis服务
/usr/redis/bin/redis-server /usr/rediscluster/redis7000/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7001/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7002/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7003/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7004/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7005/redis.conf
4、连接一个redis 服务
#使用本地的ip
/opt/redis/redisCluster/redis-cli -h 192.168.1.19 -p 7000 -a redis123456
查看redis服务的启动情况:ps -ef | grep redis
5、执行创建cluster 集群命令
注意:redis设置了密码,后面增加-a redis123456输入redis密码
#(建议最好使用服务器的ip地址搭建)上面命令自动建立主从关系(自动建立卡槽),最后数字 1 的意思是:每个主节点有1个从节点
/opt/redis/redisCluster/redis-cli --cluster create 192.168.1.19:7000 192.168.1.19:7001 192.168.1.19:7002 192.168.1.19:7003 192.168.1.19:7004 192.168.1.19:7005 --cluster-replicas 1 -a redis123456
# 执行之后,有个确定操作,输入 yes 继续执行

6、这时候集群搭建完成,连接集群测试
需要使用redis-cli -c命令连接集群中6个节点中任何一个节点都可以,注意和之前的连接参数有点不同redis-cli命令后面多了一个-c参数,表示采用集群的方式连接,连上以后,然后使用cluster nodes可以查看集群节点信息,如下
/opt/redis/redisCluster/redis-cli -h 192.168.1.19 -p 7000 -a redis123456 -c
扩容:当redis数据量日渐增长,当内存不够用的时候,这时候就需要集群扩容了,cluster集群扩容可以增加内存也可以增加节点,因为redis数据都是存在内存中。
原理:原来的节点算好要拿出多少的槽位给新加的节点,新加的节点准备导入的槽位,准备的前提条件就是加入集群,一切准备就绪后,主节点将划分出来的槽位分配给新节点,然后将相关槽位的数据迁移到新的节点
操作步骤:
1、在前面的基础上新建两个文件夹
/opt/redis/redisCluster/redis-server /opt/redis/redisCluster/redis7006/redis.conf
/opt/redis/redisCluster/redis-server /opt/redis/redisCluster/redis7007/redis.conf
启动这两个端口的redis服务
2、把7006节点增加到集群中
#最后面7000节点不是一定的,可以写7000-7005任意一节点皆可
/opt/redis/redisCluster/redis-cli --cluster add-node 192.168.1.19:7006 192.168.1.19:7000 -a redis123456

3、新增一个从节点 为7007(后面跟上7006主节点的节点id)
先查看7006在集群中的ID
# 连接集群
/opt/redis/redisCluster/redis-cli -h 192.168.1.19 -p 7000 -c -a redis123456
cluster nodes
找到7006的集群ID,那么执行add-node即可
#172.16.166.122:7000 不是一定的,可以填7000-7005任意一端口,执行增加从节点的命令
/opt/redis/redisCluster/redis-cli --cluster add-node 192.168.1.19:7007 192.168.1.19:7000 --cluster-slave --cluster-master-id e78d7d3f972b425538ee4f1abadc20da12bfb814 -a redis123456
到这里扩容2个节点7006和7007已经完成一半,但还没有分配哈希槽,无法进行写操作,下面开始分配卡槽到
4、分配卡槽
总哈希槽是16384,平均分给3组,每组是5461,现在加多一组到集群,变成4组,平均每组 16384/4=4096,那么每组要抽取多少哈希槽给7006呢?4096/3=1365
执行reshard进行从3组中抽取1365哈希缓存分配给7006
/opt/redis/redisCluster/redis-cli --cluster reshard 192.168.1.19:7000 -a redis123456
执行后会弹出“您要移动多少个插槽(从 1 到 16384)” 输入上面算出来的结果4096

输入4096后会弹出接收者ID,我们输入上面找到的7006的集群ID

然后会弹出分配方式,是平均分配还是从其中一台抽取,我们使用平均分配 all

后面输入 yes即可

到这里扩容分哈希槽就完成了,接下来看分配的结果:
/opt/redis/redisCluster/redis-cli -h 192.168.1.19 -p 7000 -c -a redis123456
cluster nodes
到这里扩容完成

Redis槽位缩容(执行下面命令)from后面是要删除的redis节点,to后面是卡槽到的redis节点。cluster-slots移动多少卡槽过去
# 删除 7006 节点的卡槽,移动到 7000 节点上
/opt/redis/redisCluster/redis-cli -a redis123456 --cluster reshard 192.168.1.19:7000 --cluster-from e78d7d3f972b425538ee4f1abadc20da12bfb814 --cluster-to 9550217baa8a823bb7547ed0f15ea630e22f440a --cluster-slots 1365
移除集群中的节点
/opt/redis/redisCluster/redis-cli -a redis123456 --cluster del-node 192.168.1.19:7000 938ed8f46c9eac385a92e9f80c4ebddc8fdedaaa
将添加的主从节点都移除之后,至此结束。