1. 为什么要分片存储
2. 官方集群方案
redis cluster是Redis的分布式集群解决方案,在3.0版本推出后有效地解决了redis分布式方面的需求实现了数据在多个Redis节点之间自动分片、故障自动转移、扩容机制等功能。
3. 搭建集群
为了方便演示(其实是没有那么多服务器),这里仅使用一台服务器来演示,但是使用了6个端口,服务器的具体信息如下:
- matser1 192.168.1.7 6381
- master2 192.168.1.7 6382
- master3 192.168.1.7 6383
- slave1 192.168.1.7 6384 是master1的从服务器
- slave2 192.168.1.7 6385 是master2的从服务器
- slave3 192.168.1.7 6386 是master3的从服务器
- 配置6381.conf,其他的5个文件配置是类似的,只是端口不一样而已
# 配置文件进行了精简,完整配置可自行和官方提供的完整conf文件进行对照。端口号自行对应修改
#后台启动的意思
daemonize yes
#端口号
port 6381
# IP绑定,redis不建议对公网开放,直接绑定0.0.0.0没毛病
bind 0.0.0.0
# redis数据文件存放的目录
dir /etc/softwares/redis-5.0.14/data
# 开启AOF
appendonly yes
# 开启集群
cluster-enabled yes
# 会自动生成在上面配置的dir目录下
cluster-config-file nodes-6381.conf
cluster-node-timeout 5000
# 这个文件会自动生成
pidfile /var/run/redis_6381.pid
- 启动6个服务
redis-server /etc/softwares/redis-5.0.14/conf/6381.conf
redis-server /etc/softwares/redis-5.0.14/conf/6382.conf
redis-server /etc/softwares/redis-5.0.14/conf/6383.conf
redis-server /etc/softwares/redis-5.0.14/conf/6384.conf
redis-server /etc/softwares/redis-5.0.14/conf/6385.conf
redis-server /etc/softwares/redis-5.0.14/conf/6386.conf
- 创建集群
redis-cli --cluster create 192.168.1.7:6381 192.168.1.7:6382 192.168.1.7:6383 \
192.168.1.7:6384 192.168.1.7:6385 192.168.1.7:6386 \
--cluster-replicas 1
- 执行结果如下
[root@lazyfennec conf]# redis-cli --cluster create 192.168.1.7:6381 192.168.1.7:6382 \
192.168.1.7:6383 192.168.1.7:6384 192.168.1.7:6385 192.168.1.7:6386 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.1.7:6385 to 192.168.1.7:6381
Adding replica 192.168.1.7:6386 to 192.168.1.7:6382
Adding replica 192.168.1.7:6384 to 192.168.1.7:6383
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: d894444929c99bcaa72c5747577ab5b8dfa773a7 192.168.1.7:6381
slots:[0-5460] (5461 slots) master
M: 7aec5dec7fb1d143cce7ce6c106db8d6cc0ae71d 192.168.1.7:6382
slots:[5461-10922] (5462 slots) master
M: 16ddd1d0d8abed54f32291b26f79491f350f811c 192.168.1.7:6383
slots:[10923-16383] (5461 slots) master
S: 0d82201d62488c66b8a731e1bb2eafa35c66ed24 192.168.1.7:6384
replicates 7aec5dec7fb1d143cce7ce6c106db8d6cc0ae71d
S: 7e91ec78210c44fa29192266e03ba2436d35826c 192.168.1.7:6385
replicates 16ddd1d0d8abed54f32291b26f79491f350f811c
S: 3807b2c8d3b60d4e59b886b5ea9c8e4edddb0268 192.168.1.7:6386
replicates d894444929c99bcaa72c5747577ab5b8dfa773a7
# 这里自动设置主从
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.
>>> Performing Cluster Check (using node 192.168.1.7:6381)
M: d894444929c99bcaa72c5747577ab5b8dfa773a7 192.168.1.7:6381
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 7aec5dec7fb1d143cce7ce6c106db8d6cc0ae71d 192.168.1.7:6382
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 3807b2c8d3b60d4e59b886b5ea9c8e4edddb0268 192.168.1.7:6386
slots: (0 slots) slave
replicates d894444929c99bcaa72c5747577ab5b8dfa773a7
S: 0d82201d62488c66b8a731e1bb2eafa35c66ed24 192.168.1.7:6384
slots: (0 slots) slave
replicates 7aec5dec7fb1d143cce7ce6c106db8d6cc0ae71d
M: 16ddd1d0d8abed54f32291b26f79491f350f811c 192.168.1.7:6383
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 7e91ec78210c44fa29192266e03ba2436d35826c 192.168.1.7:6385
slots: (0 slots) slave
replicates 16ddd1d0d8abed54f32291b26f79491f350f811c
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
4. 集群检测和测试
1. 检查集群,查看所有节点信息
redis-cli -c -h 192.168.1.7 -p 6381 cluster nodes
- 执行后的结果
[root@lazyfennec conf]# redis-cli -c -h 192.168.1.7 -p 6381 cluster nodes
7aec5dec7fb1d143cce7ce6c106db8d6cc0ae71d 192.168.1.7:6382@16382 master - 0 1662111831558 2 connected 5461-10922
3807b2c8d3b60d4e59b886b5ea9c8e4edddb0268 192.168.1.7:6386@16386 slave d894444929c99bcaa72c5747577ab5b8dfa773a7 0 1662111832059 6 connected
0d82201d62488c66b8a731e1bb2eafa35c66ed24 192.168.1.7:6384@16384 slave 7aec5dec7fb1d143cce7ce6c106db8d6cc0ae71d 0 1662111833062 4 connected
d894444929c99bcaa72c5747577ab5b8dfa773a7 192.168.1.7:6381@16381 myself,master - 0 1662111832000 1 connected 0-5460
16ddd1d0d8abed54f32291b26f79491f350f811c 192.168.1.7:6383@16383 master - 0 1662111833062 3 connected 10923-16383
7e91ec78210c44fa29192266e03ba2436d35826c 192.168.1.7:6385@16385 slave 16ddd1d0d8abed54f32291b26f79491f350f811c 0 1662111832561 5 connected
2. 测试Redis Cluster的一种简单方法是使用redis-cli命令行实用程序,-c 是支持cluster重定向
[root@lazyfennec conf]# redis-cli -c -h 192.168.1.7 -p 6381
192.168.1.7:6381> set hello "1"
OK
192.168.1.7:6381> set is_key "1"
-> Redirected to slot [13696] located at 192.168.1.7:6383
OK
192.168.1.7:6383> get hello
-> Redirected to slot [866] located at 192.168.1.7:6381
"1"
192.168.1.7:6381> get is_key
-> Redirected to slot [13696] located at 192.168.1.7:6383
"1"
3. 查看一个key属于哪一个节点
cluster keyslot [key]
- 执行结果
192.168.1.7:6383> cluster keyslot key
(integer) 12539
192.168.1.7:6383> cluster keyslot hello
(integer) 866
192.168.1.7:6383> cluster keyslot is_key
(integer) 13696
5. 集群slot数量整理 reshard
可以查看所有这个命令和子命令的帮助信息
redis-cli --cluster help
默认是master平均分了0-16383的所有虚拟slot
可以进行调整,部分节点放多一点slot(槽或者位置)。
redis-cli --cluster reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes
重新检查集群
[root@node3 redis]# /usr/local/redis/bin/redis-cli --cluster check 192.168.1.7:6382
192.168.1.7:6382 (764500b8...) -> 0 keys | 5462 slots | 1 slaves.
192.168.1.7:6383 (93293699...) -> 1 keys | 5461 slots | 1 slaves.
192.168.1.7:6381 (68326caa...) -> 1 keys | 5461 slots | 1 slaves.
[OK] 2 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 192.168.1.7:6382)
M: 764500b86fadebd535ac2b5b778a73486fe7d2b7 192.168.1.7:6382
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: a842c5188c441453fd303520424132d45914fe5b 192.168.1.7:6384
slots: (0 slots) slave
replicates 764500b86fadebd535ac2b5b778a73486fe7d2b7
M: 93293699b8966ccc202bb29c659a9f60e26e4c86 192.168.1.7:6383
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 42806ad3f740f639ec321b78ea492c42a4040176 192.168.1.7:6386
slots: (0 slots) slave
replicates 68326caa0238cb877afc3e6df23eb92558fcbc3c
S: 0a7061773b2512c91b6173bc27451b19fe02f269 192.168.1.7:6385
slots: (0 slots) slave
replicates 93293699b8966ccc202bb29c659a9f60e26e4c86
M: 68326caa0238cb877afc3e6df23eb92558fcbc3c 192.168.1.7:6381
slots:[0-5460] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
7. 测试自动故障转移
cluster集群不保证数据一致,数据也可能丢失
首先是运行客户端不断的写入或读取数据,以便能够发现问题
然后是模拟节点故障:找一个主节点关闭,主从故障切换的过程中,这个时间端的操作,客户端而言,只能是失败
官方描述 https://redis.io/topics/cluster-spec
There is always a window of time when it is possible to lose writes during partitions.
分区的时间窗口内总是有可能丢失写操作。
使用的例子
- 创建Config类,配置Bean
package cn.lazyfennec.cache.redis;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.Arrays;
@Configuration
// 在cluster环境下生效
@Profile("a7_cluster")
class ClusterAppConfig {
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(redisConnectionFactory);
return stringRedisTemplate;
}
@Bean
public JedisConnectionFactory redisConnectionFactory() {
System.out.println("加载cluster环境下的redis client配置");
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(Arrays.asList(
"192.168.1.7:6381",
"192.168.1.7:6382",
"192.168.1.7:6383",
"192.168.1.7:6384",
"192.168.1.7:6385",
"192.168.1.7:6386"
));
// 自适应集群变化
return new JedisConnectionFactory(redisClusterConfiguration);
}
}
- 创建service
package cn.lazyfennec.cache.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
@Service
@Profile("cluster")
public class ClusterService {
@Autowired
private StringRedisTemplate template;
public void set(String userId, String userInfo) {
template.opsForValue().set(userId, userInfo);
}
}
- 创建测试类
package cn.lazyfennec.cache.redis;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.concurrent.TimeUnit;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
@ActiveProfiles("cluster") // 设置profile
// 集群对于客户端而言,基本是无感知的
public class ClusterServiceTests {
@Autowired
ClusterService clusterService;
@Test
public void setTest() {
clusterService.set("neco", "hahhhhh");
clusterService.set("a", "1");
clusterService.set("foo", "bar");
}
// 测试cluster集群故障时的反应
@Test
public void failoverTest() {
while (true) {
try {
long i = System.currentTimeMillis();
clusterService.set("neco", i + "");
// delay 10ms
TimeUnit.MILLISECONDS.sleep(10);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
}
8. 手动故障转移
可能某个节点需要维护(机器下线、硬件升级、系统版本调整等等场景),需要手动的实现转移
在slave节点上执行命令,下面的命令会将slave变成master,然后将master变成slave
cluster failover
注:CLUSTER help 可以看到帮助文档和简介。 相对安全的做法
9. 扩容
1、 启动新节点
redis-server /etc/softwares/redis-5.0.14/conf/6387.conf
2、 加入到已经存在的集群作为master
redis-cli --cluster add-node 192.168.1.7:6387 192.168.1.7:6382
本质就是发送一个新节点通过 CLUSTER MEET命令加入集群
新节点没有分配hash槽
3、 加入到已经存在的集群作为slave
redis-cli --cluster add-node 192.168.1.7:7006 192.168.1.7:7000 --cluster-slave
可以手工指定master,否则就是选择一个slave数量较少的master
redis-cli --cluster add-node 192.168.1.7:7006 192.168.1.7:7000 --cluster-slave --cluster-master-id <node-id>
还可以将空master,转换为slave
cluster replicate <master-node-id>
4、 检查集群
redis-cli --cluster check 192.168.1.7:6382
10. 缩容(删除节点)
注意:删除master的时候要把数据清空或者分配给其他主节点
redis-cli --cluster del-node 192.168.1.7:6381 <node-id>
11. 关心的问题
1、增加了slot槽的计算,是不是比单机性能差?
共16384个槽,slots槽计算方式公开的,HASH_SLOT = CRC16(key) mod 16384。
为了避免每次都需要服务器计算重定向,优秀的java客户端都实现了本地计算,并且缓存服务器slots分配,有变动时再更新本地内容,从而避免了多次重定向带来的性能损耗。(结合画图过程理解)
2、redis集群大小,到底可以装多少数据?
理论是可以做到16384个槽,每个槽对应一个实例,但是redis官方建议是最大1000个实例。存储足够大了
3、集群节点间是怎么通信的?
每个Redis群集节点都有一个额外的TCP端口,每个节点使用TCP连接与每个其他节点连接。检测和故障转移这些步骤基本和哨兵模式类似(毕竟是同一个软件,同一个作者设计)。
4、ask和moved重定向的区别
重定向包括两种情况
- 若确定slot不属于当前节点,redis会返回moved。
- 若当前redis节点正在处理slot迁移,则代表此处请求对应的key暂时不在此节点,返回ask,告诉客户端本次请求重定向。
5、数据倾斜和访问倾斜的问题
倾斜导致集群中部分节点数据多,压力大。解决方案分为前期和后期:
- 前期是业务层面提前预测,哪些key是热点,在设计的过程中规避。
- 后期是slot迁移,尽量将压力分摊(slot调整有自动rebalance、reshard和手动)。
6、slot手动迁移怎么做?
迁移过程如下,大致描述如下:
在迁移目的节点执行cluster setslot <slot> IMPORTING<node ID>命令,指明需要迁移的slot和迁移源节点。
在迁移源节点执行cluster setslot <slot> MIGRATING<node lD>命令,指明需要迁移的slot和迁移目的节点。
在迁移源节点执行cluster getkeysinslot获取该slot的key列表。
在迁移源节点执行对每个key执行migrate命令,该命令会同步把该key迁移到目的节点。
在迁移源节点反复执行cluster getkeysinslot命令,直到该slot的列表为空。
在迁移源节点和目的节点执行cluster setslot <slot> NODE<node ID>,完成迁移操作。
7、节点之间会交换信息,传递的消息包括槽的信息,带来带宽消耗。
注意:避免使用大的一个集群,可以分多个集群。
8、Pub/Sub发布订阅机制
注意∶对集群内任意的一个节点执行publish发布消息,这个消息会在集群中进行传播,其他节点接收到发布的消息。
9、读写分离
- redis-cluster默认所有从节点上的读写,都会重定向到key对接槽的主节点上。
- 可以通过readonly设置当前连接可读,通过readwrite取消当前连接的可读状态。
注意:主从节点依然存在数据不一致的问题
如果觉得有收获,欢迎点赞和评论,更多知识,请点击关注查看我的主页信息哦~













网友评论