Redis 集群
1 redis集群简介
1.1 集群的概念
所谓的集群,就是通过添加服务器的数量,提供相同的服务,从而让服务器达到一个稳定、高效的状态(高可用)。
1.2 使用redis集群的必要性
问题:我们已经部署好了redis,并且能启动一个redis,实现数据的读写,为什么还要学习redis集群?
- 单个redis存在不稳定性。当redis服务宕机了,就没有可用的服务了。
- 单个redis的读写能力是有限的。
redis集群是为了强化redis的读写能力。
1.3 如何学习redis集群
- redis集群中,每一个redis称之为一个节点。
- redis集群中,有两种类型的节点:主节点(master)、从节点(slave)。
- redis集群,是基于redis主从复制实现。
所以,学习redis集群,就是从学习redis主从复制模型开始的。
2 redis主从复制
2.1 概念
主从复制模型中,有多个redis节点。其中,有且仅有一个为主节点Master。从节点Slave可以有多个。只要网络连接正常,Master会一直将自己的数据更新同步给Slaves,保持主从同步。
2.2 特点
- 主节点Master可读、可写
- 从节点Slave只读。(read-only)
因此,主从模型可以提高读的能力,在一定程度上缓解了写的能力。因为能写仍然只有Master节点一个,可以将读的操作全部移交到从节点上,变相提高了写能力。
2.3 基于配置实现
2.3.1 需求
节点 | 端口 |
---|---|
主节点 | 6380 |
从节点(两个) | 6381、6382 |
2.3.2 配置步骤
- 在
/usr/local
目录下,创建一个/redis/master-slave
目录 - 在
master-slave
目录下,创建三个子目录 6380、6381、6382 - 依次拷贝redis解压目录下的 redis.conf 配置文件,到这三个子目录中
- 进入 6380 (master) 目录,修改 redis.conf ,将 port 端口修改成 6380 即可
- 进入 6381 (slave) 和 6382 目录,分别修改 redis.conf,将 port 端口改成 6381、6382,同时指定开启主从复制。
2.3.3 测试
依次启动主从节点,主节点的日志中会显示从节点的连入。经测试可以看到,主节点可以读写,从节点默认只能读不能写。
3 Sentinel哨兵模式
3.1 主从模式的缺陷
当主节点宕机了,整个集群就没有可写的节点了。由于从节点上备份了主节点的所有数据,那在主节点宕机的情况下,如果能够将从节点变成一个主节点,是不是就可以解决这个问题了呢?是的,这个就是Sentinel哨兵的作用。
3.2 哨兵的任务
Redis 的 Sentinel 系统用于管理多个 Redis 服务器(instance), 该系统执行以下三个任务:
- 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
- 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
- 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会进行选举,将其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。
3.2.1 监控(Monitoring)
- Sentinel可以监控任意多个Master和该Master下的Slaves。(即多个主从模式)
- 同一个哨兵下的、不同主从模型,彼此之间相互独立。
- Sentinel会不断检查Master和Slaves是否正常。
3.2.2 自动故障切换(Automatic failover)
3.2.2.1 Sentinel网络
监控同一个Master的Sentinel会自动连接,组成一个分布式的Sentinel网络,互相通信并交换彼此关于被监视服务器的信息。下图中,三个监控s1的Sentinel,自动组成Sentinel网络结构。
3.2.2.2 故障切换的过程
- 投票(半数原则):当任何一个Sentinel发现被监控的Master下线时,会通知其它的Sentinel开会,投票确定该Master是否下线(半数以上,所以sentinel通常配奇数个)。
- 选举:当Sentinel确定Master下线后,会在所有的Slaves中,选举一个新的节点,升级成Master节点。其它Slaves节点,转为该节点的从节点。
- 当原Master节点重新上线后,自动转为当前Master节点的从节点。
3.3 哨兵模式部署
3.3.1 需求
前提:已经存在一个正在运行的主从模式。
配置三个Sentinel实例,监控同一个Master节点。
3.3.2 配置Sentinel
- 在
/usr/local
目录下,创建/redis/sentinels/
目录 - 在
/sentinels
目录下,以次创建 s1、s2、s3 三个子目录 - 依次拷贝 redis 解压目录下的 sentinel.conf 文件,到这三个子目录中
- 依次修改s1、s2、s3子目录中的 sentinel.conf 文件,修改端口,并指定要监控的主节点。(从节点不需要指定,sentinel会自动识别)
依次启动三个哨兵后,可以看到日志输出
3.3.3 测试
- 手动关闭 6380 节点后,发现重新指定新主节点,并将另外两个节点作为从节点加入
- 再次上线 6380,发现被指定为从节点,只能读不能写
4 Redis-cluster集群
4.1 哨兵模式的缺陷
在哨兵模式中,仍然只有一个Master节点。当并发写请求较大时,哨兵模式并不能缓解写压力。我们知道只有主节点才具有写能力,那如果在一个集群中,能够配置多个主节点,是不是就可以缓解写压力了呢?是的。这个就是redis-cluster集群模式。
4.2 Redis-cluster集群概念
- 由多个Redis服务器组成的分布式网络服务集群;
- 集群之中有多个Master主节点,每一个主节点都可读可写;
- 节点之间会互相通信,两两相连;
- Redis集群无中心节点。
4.3 集群节点复制
在Redis-Cluster集群中,可以给每一个主节点添加从节点,主节点和从节点直接遵循主从模型的特性。当用户需要处理更多读请求的时候,添加从节点可以扩展系统的读性能。
4.4 故障转移
Redis集群的主节点内置了类似Redis Sentinel的节点故障检测和自动故障转移功能,当集群中的某个主节点下线时,集群中的其他在线主节点会注意到这一点,并对已下线的主节点进行故障转移。
集群进行故障转移的方法和Redis Sentinel进行故障转移的方法基本一样,不同的是,在集群里面,故障转移是由集群中其他在线的主节点负责进行的,所以集群不必另外使用Redis Sentinel。
4.5 集群分片策略
Redis-cluster分片策略,是用来解决key存储位置的。
集群将整个数据库分为16384个槽位slot,所有key-value数据都存储在这些slot中的某一个上。一个slot槽位可以存放多个数据,key的槽位计算公式为:slot_number=crc16(key)%16384
,其中 crc16 为 16 位的循环冗余校验和函数。
集群中的每个主节点都可以处理0个至16383个槽,当16384个槽都有某个节点在负责处理时,集群进入上线状态,并开始处理客户端发送的数据命令请求。
4.6 集群redirect转向
由于Redis集群无中心节点,请求会随机发给任意主节点;主节点只会处理自己负责槽位的命令请求,其它槽位的命令请求,该主节点会返回客户端一个转向错误;客户端根据错误中包含的地址和端口重新向正确的负责的主节点发起命令请求。
4.7 集群搭建
4.7.1 准备工作
安装ruby环境
redis 集群管理工具 redis-trib.rb 依赖 ruby 环境,首先需要安装 ruby 环境:
yum -y install ruby
yum -y install rubygems
安装ruby和redis的接口程序
拷贝redis-3.0.0.gem至/usr/local下,执行安装:
gem install /usr/local/redis-3.0.0.gem
或者直接用 gem 在线安装
gem install reids
redis-trib.rb 工具
官方提供了此工具用于挂历 redis 集群,该工具就在解压目录的 src 目录下
4.7.2 集群规划
Redis集群最少需要6个节点,可以分布在一台或者多台主机上。以下测试为在一台主机上创建伪分布式集群,不同的端口表示不同的redis节点,如下:
- 主节点:192.168.56.3:7001 192.168.56.3:7002 192.168.56.3:7003
- 从节点:192.168.56.3:7004 192.168.56.3:7005 192.168.56.3:7006
在 /usr/local/redis 下创建 redis-cluster 目录,其下创建7001、7002...7006目录,复制 redis.conf 配置文件到每个文件夹,并配置
# 必选配置
port 700X
bind 192.168.X.X
cluster-enabled yes
# 可选配置
daemonized yes
logfile /usr/local/redis/redis-cluster/700X/node.log
4.7.3 启动每个结点redis服务
依次以700X下的redis.conf,启动redis节点。(必须指定redis.conf文件)
注意,需要分别进入各个文件夹启动,不然会报 cluster config file 已经被使用的错误
4.7.4 执行创建集群命令
进入到 redis 源码存放目录 src 目录下,执行redis-trib.rb,此脚本是ruby脚本,它依赖ruby环境。
./redis-trib.rb create --replicas 1 192.168.163.88:7001 192.168.163.88:7002 192.168.163.88:7003 192.168.163.88:7004 192.168.163.88:7005 192.168.163.88:7006
这里发现,最新版 redis 已经取消对 redis-trib.rb 的支持,采用示例方法创建集群
新版本的命令大全
4.7.5 查询集群信息
集群创建成功登陆任意redis结点查询集群中的节点情况。
- -c:表示以集群方式连接redis
- -h:指定host ip地址
- -p:指定端口号
- cluster nodes:查询集群结点信息
- cluster info:查询集群状态信
4.8 集群管理
4.8.1 添加主节点
4.8.1.1 节点规划
集群创建成功后可以向集群中添加节点,下面是添加一个master主节点,添加7007节点。
配置和启动新节点的步骤略
执行下边命令添加节点(第一个地址为新节点,第二个地址为 cluster 集群中的任意一个节点地址):
./redis-trib.rb add-node 192.168.23.3:7007 192.168.23.3:7001 //已过时
redis-cli --cluster add-node 192.168.163.88:7007 192.168.163.88:7002
运行 redis-cli --cluster check 192.168.163.88:7001
检查状态,发现新节点作为主节点加入,但没有 slot 分配给它。
4.8.1.2 slot槽重新分配
添加完新的主节点后,需要对主节点进行slot槽分配,这样该主节才可以存储数据。
第一步:连接上集群
连接集群中任意一个可用节点都行
redis-cli --cluster reshard 192.168.163.88:7001
第二步:输入要分配的槽数量
输入 500表示要分配500个槽
第三步:输入接收槽的结点id
这里输入的是新加入的节点 7007
第四步:输入源结点id
这里我选择从 7001 作为源节点获取 500 个 slot,也可以输入 all 表示从所有主节点中平均获取。可以输入多个源节点,输入done 表示输入结束。
第五步:检查结果
如果只是从一个源里转移,可以使用一句语句完成操作
redis-cli --cluster reshard 192.168.163.88:7001 --cluster-from 95252ffbf34bb114b859ed7da8a312e28347d5c1 --cluster-to e272188208df9d9080d41a89a0fffd49e503879c --cluster-slots 500
4.8.2 添加从节点
为新增的主节点添加从节点,将 7008 作为 7007 的从节点。
redis-cli --cluster add-node 192.168.163.88:7008 192.168.163.88:7001 --cluster-slave --cluster-master-id e272188208df9d9080d41a89a0fffd49e503879c
若不指定 --cluster-master-id 同时声明了 --cluster-slave,则默认会添加为第二个地址的从节点
若 7008 下面已有 nodes.conf ,添加时可能会报错, 解决方法是删除该文件后再添加
4.8.3 删除节点
使用命令
redis-cli --cluster del-node 192.168.163.88:7001 d5d9af031a714c4fe334e8950de46add16c0e6df
第一个地址为 cluster 任一节点, 后面 id 为需要删除的节点 id
需要注意的是,若删除的节点为主节点,需要将其所拥有的 slot 分配出去后才能删除,不然会报如下错误
将 7007 的 slot 转移回 7001 后删除
5 java程序连接redis集群
5.1 连接步骤
5.1.1 第一步:创建项目,导入jar包
5.1.2 第二步:创建redis集群的客户端
import org.junit.Test;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import java.util.HashSet;
import java.util.Set;
public class ClusterTest {
@Test
public void ClusterConnectionTest() {
//创建 set 集合封装所有节点信息
Set<HostAndPort> nodes = new HashSet<>();
//只需要添加一个节点即可,会自动搜索其它节点
nodes.add(new HostAndPort("192.168.163.88", 7001));
/*nodes.add(new HostAndPort("192.168.163.88", 7002));
nodes.add(new HostAndPort("192.168.163.88", 7003));
nodes.add(new HostAndPort("192.168.163.88", 7004));
nodes.add(new HostAndPort("192.168.163.88", 7005));
nodes.add(new HostAndPort("192.168.163.88", 7006));*/
//使用节点创建一个 JedisCluster 对象
JedisCluster jedisCluster = new JedisCluster(nodes);
//测试连接结果
System.out.println(jedisCluster.get("wtf"));
}
@Test
public void RedisConnectionTest(){
Jedis jedis = new Jedis("192.168.163.88", 7001);
System.out.println(jedis.ping());
String hello = jedis.get("hello"); //hello 存在 7001
System.out.println(hello);
//采用普通方式连接,若数据不是存在此节点,会报错
/*String wtf = jedis.get("wtf"); //wtf 存在 7002
System.out.println(wtf);*/
}
}
5.2 注意事项
连接Redis集群时,需要修改防火墙,开方每一个redis节点的端口。
说明:如果要开发一个范围的端口,可以使用冒号来分割,即: 7001:7008,表示开发7001-7008之间所有的端口,或者使用 setup 工具设置