在多个Redis实列节点间共享数据,每个节点都与其他所有节点相连,形成网状结构。
redis下载安装: Redis
redis cluster官方教程:Redis cluster tutorial – Redis
目录
相关概念
数据分片
主从复制模型
搭建并使用
MOVED 重定向
测试主从复制
故障转移
失效检测
测试自动故障转移
重新分片
追加、删除节点
增加主节点
增加从节点
删除节点
Spring data redis 连接集群
相关概念
数据分片
Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。集群的每个主节点负责一部分hash槽。
主从复制模型
为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型。主节点可读可写,从节点只能进行读。
搭建并使用
- 创建一个cluster-test文件夹,并在其中创建6个文件夹,以端口命名如:7000,7001,7002,7003,7004,7005
命令:
mkdir cluster-test
cd cluster-test
mkdir 7000 7001 7002 7003 7004 7005
- 在文件夹 7000 至 7005 中, 各创建一个 redis.conf 文件
#端口
port 7000
#开启集群模式
cluster-enabled yes
#集群节点配置文件默认值为 nodes.conf.节点配置文件无须人为修改, 它由 Redis 集群在
#启动时创建, 并在有需要时自动进行更新。
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
#以后台模式运行
daemonize yes
requirepass 123456
#主节点密码(当主节点使用了requirepass ,从节点需要通过masterauth 来从主节点同步数据)
masterauth 123456
- 在文件夹 7000 至 7005 中, 各放置一个redis-server ,并将6个redis实例启动起来
命令:redis-server redis.conf
4、现在我们已经有了六个正在运行中的 Redis 实例, 接下来我们需要使用这些实例来创建集群
5、通过redis-cli创建集群
命令:redis-cli --cluster create 127.0.0.1:7000 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 --cluster-replicas 1 -a 123456
注:选项--cluster-replicas 1 表示我们希望为集群中的每个主节点创建一个从节点,127.0.0.1需要根据实际情况填写
6、连接集群
redis-cli -c -p 7000
连接任意节点查看集群节点状态
redis-cli -p 7000 cluster nodes
MOVED 重定向
Redis客户端可以向集群中的任意节点发送命令,当这个命令是集群中某节点可执行的,收到命令的节点会根据键计算对应的hash槽,如果hash槽正好在当前节点,则执行命令。否则给客户端返回一个包含这个hash槽的节点信息的错误信息以便客户端可重定向到新的节点去处理命令。
测试主从复制
1、连接任意节点: redis-cli -c -p 7000 -a 123456
2、写入一个键值对(集群会自动重定向到对应的节点)
3、重新连接刚才执行了写入操作的主节点的一个从节点
4、先使用readonly命令,再执行读操作,即可查询出数据
故障转移
失效检测
每个节点对其他每个节点都两个标识用于失效检测,pfail 和fail。pfail表示可能失效,即在cluster-node-timeout
节点间通过定时发ping消息判断彼此是否在线,如A节点给B节点得消息在cluster-node-timeout
客观下线的主节点的从节点承担故障恢复的义务。
测试自动故障转移
当主节点下线后,从节点会自动被选举为新的主节点。
可使用redis-cli -p 7000 cluster nodes命令检查主节点线下前后的区别。
重新分片
重新分片就是将槽移动到别的节点。
使用命令:
redis-cli --cluster reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots>
<host>:<port>
<node-id>
<node-id>
<number of slots>
追加、删除节点
增加主节点
- 重新启动一个节点
- 添加一个主节点 redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
(127.0.0.1:7000可以是集群中任意节点,实际上就是发送了meet消息,这个新的主节点目前还没有分配槽,所以不存储数据,只支持查询重定向到其他节点)
3、进行重新分片分配slots。
增加从节点
- 重新启动一个节点
- 添加从节点 redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave
(此时会随机将新节点添加为从节点较少的主节点的从节点)
- redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave --cluster-master-id <node-id>
(可指定主节点id)
删除节点
redis-cli --cluster del-node 127.0.0.1:7000 <node-id>
注:随机连接一个节点进行删除操作,指定要被删除的节点的id。如果删除的节点是主节点则需要保证主节点是空的。
Spring data redis 连接集群
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisClusterConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class AppConfig {
/**
* 节点信息
*/
@Autowired
ClusterConfigurationProperties clusterProperties;
/**
* 连接工厂
*/
@Bean
public RedisConnectionFactory connectionFactory() {
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(clusterProperties.getNodes());
redisClusterConfiguration.setPassword("123456");
return new LettuceConnectionFactory(redisClusterConfiguration);
}
/**
* 1、使用连接的方式
*/
@Bean
public RedisClusterConnection redisClusterConnection(RedisConnectionFactory connectionFactory) {
RedisClusterConnection clusterConnection = connectionFactory.getClusterConnection();
return clusterConnection;
}
/**
* 2、使用模板的方式
*/
@Bean
RedisTemplate<String, ?> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, ?> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}