美文网首页
Redis集群分片存储 - cluster

Redis集群分片存储 - cluster

作者: 右耳菌 | 来源:发表于2022-09-01 18:46 被阅读0次

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取消当前连接的可读状态。
    注意:主从节点依然存在数据不一致的问题

如果觉得有收获,欢迎点赞和评论,更多知识,请点击关注查看我的主页信息哦~

相关文章

网友评论

      本文标题:Redis集群分片存储 - cluster

      本文链接:https://www.haomeiwen.com/subject/bbrenrtx.html