简介

  1. 主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:
    • 海量数据存储问题、高并发写的问题。
  2. 使用分片集群可以解决上述问题,如图:

  1. 分片集群特征:
    • 集群中有多个master,每个master保存不同数据。
    • 每个master都可以有多个slave节点。
    • master之间通过ping监测彼此健康状态。
    • 客户端请求可以访问集群任意节点,最终都会被转发到正确节点。

散列插槽

插槽原理

  1. Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,查看集群信息时就能看到。
M: xxxxxxxxxxxxxxxxxxxxxxxx 172.30.1.2:7001
    slots:[0-5460] (5461 slots) master
M: xxxxxxxxxxxxxxxxxxxxxxxx 172.30.1.2:7002
    slots:[5461-10922] (5462 slots) master
M: xxxxxxxxxxxxxxxxxxxxxxxx 172.30.1.2:7003
    slots:[10923-16383] (5461 slots) master
  1. 数据key不是与节点绑定,而是与插槽绑定。redis会根据key的有效部分计算插槽值,分两种情况:
    • key中包含 "{}",且 "{}" 中至少包含1个字符,"{}" 中的部分是有效部分。
    • key中不包含 "{}",整个key都是有效部分。
  2. 例如:key是num,那么就根据num计算,如果是{itcast}num,则根据itcast计算。计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值。
    • 在7001这个节点执行set a 1时,对a做hash运算,对16384取余,得到的结果是15495,因此要存储到7003节点。
    • 到了7003后,执行get num时,对num做hash运算,对16384取余,得到的结果是2765,因此需要切换到7001节点。
$ set a 1
-> Redirected to slot [15495] located at 172.30.1.2:7003
OK
$ get num
-> Redirected to slot [2765] located at 172.30.1.2:7001
  1. Redis如何判断某个key应该在哪个实例:
    • 将16384个插槽分配到不同的实例。
    • 根据key的有效部分计算哈希值,对16384取余。
    • 余数作为插槽,寻找插槽所在实例即可。
  2. 如何将同一类数据固定的保存在同一个Redis实例:这一类数据使用相同的有效部分,例如key都以{typeId}为前缀。

集群伸缩

  1. redis-cli –cluster 提供了很多操作集群的命令,可以通过下面方式查看:
$ redis-cli --cluster help

添加新节点到集群

$ redis-cli --cluster add-node 192.168.150.101:7004 192.168.150.101:7001
  1. 查看集群状态。
$ redis-cli -p 7001 cluster nodes

转移插槽

  1. 转移插槽命令:redis-cli –cluster reshard host:port
  2. host:port 从这个位置转移到其他位置。

故障转移

自动故障转移

  1. redis 集群会自动故障转移。

手动故障转移

  1. 利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移。
  2. failover命令可以指定三种模式:
    • “":默认的流程,如图1~6歩。
    • force:省略了对offset的一致性校验。
    • takeover:直接执行第5歩,忽略数据一致性、忽略master状态和其它master的意见。

  1. 在从节点执行:
$ CLUSTER FAILOVER

搭建集群

  1. 分片集群需要的节点数量较多,这里我们搭建一个最小的分片集群,包含3个master节点,每个master包含一个slave节点。
IP PORT 角色
172.30.2.11 7001 master
172.30.2.12 7002 master
172.30.2.13 7003 master
172.30.2.21 8001 slave
172.30.2.22 8002 slave
172.30.2.23 8003 slave

redis.conf

  1. redis.conf 文件准备。github 官网下载 redis 相应版本的配置文件。
  2. 对每个redis.conf都做以下修改。分片集群的redis主从的redis.conf目前都是一样的。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
port 6379

# 开启集群功能
cluster-enabled yes

# 集群的配置文件名称,不需要我们创建,由redis自己维护
cluster-config-file /data/nodes.conf

# 节点心跳失败的超时时间
cluster-node-timeout 5000

# 持久化文件存放目录
dir /data

# 绑定地址
bind 0.0.0.0

# no 非后台守护运行
daemonize no

# 保护模式
protected-mode no

# 数据库数量
databases 1

# 日志
logfile /data/run.log

docker-compose.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
version: '3.8'

networks:
  redis-sharding:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.30.2.0/24

services:
  master1:
    container_name: master1
    image: redis:6.2.3
    volumes:
      - ./master1/conf:/usr/local/etc/redis
    ports:
      - "7001:6379"
    command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
    networks:
      redis-sharding:
        ipv4_address: 172.30.2.11

  master2:
    container_name: master2
    image: redis:6.2.3
    volumes:
      - ./master2/conf:/usr/local/etc/redis
    ports:
      - "7002:6379"
    command: [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
    networks:
      redis-sharding:
        ipv4_address: 172.30.2.12

  master3:
    container_name: master3
    image: redis:6.2.3
    volumes:
      - ./master3/conf:/usr/local/etc/redis
    ports:
      - "7003:6379"
    command: [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
    networks:
      redis-sharding:
        ipv4_address: 172.30.2.13

  replica1:
    container_name: replica1
    image: redis:6.2.3
    volumes:
      - ./replica1/conf:/usr/local/etc/redis
    ports:
      - "8001:6379"
    command: [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
    networks:
      redis-sharding:
        ipv4_address: 172.30.2.21

  replica2:
    container_name: replica2
    image: redis:6.2.3
    volumes:
      - ./replica2/conf:/usr/local/etc/redis
    ports:
      - "8002:6379"
    command: [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
    networks:
      redis-sharding:
        ipv4_address: 172.30.2.22

  replica3:
    container_name: replica3
    image: redis:6.2.3
    volumes:
      - ./replica3/conf:/usr/local/etc/redis
    ports:
      - "8003:6379"
    command: [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
    networks:
      redis-sharding:
        ipv4_address: 172.30.2.23
$ docker-compose -p redis-sharding up -d

构建集群

下面的命令都在 master1 容器里执行。

自动分配主从关系

  1. 创建了一个集群,包括三个主节点和三个从节点,每个主节点分配一个从节点作为副本,前3个ip为主节点,后3个为从节点,主节点的从节点随机分配。
$ redis-cli --cluster create 172.30.2.11:6379 172.30.2.12:6379 172.30.2.13:6379 172.30.2.21:6379 172.30.2.22:6379 172.30.2.23:6379 --cluster-replicas 1

手动分配主从关系

  1. 分配 master 集群。
$ redis-cli --cluster create 172.30.2.11:6379 172.30.2.12:6379 172.30.2.13:6379 --cluster-replicas 0
  1. 查看3个主节点的ID。
$ redis-cli -h 172.30.2.11 -p 6379 cluster nodes
  1. 将3个从节点加入集群中,其中172.30.2.11可以是三个主节点的任意一个。
$ redis-cli -h 172.30.2.21 -p 6379 cluster meet 172.30.2.11 6379
$ redis-cli -h 172.30.2.22 -p 6379 cluster meet 172.30.2.11 6379
$ redis-cli -h 172.30.2.23 -p 6379 cluster meet 172.30.2.11 6379
  1. 为每个从节点指定主节点。
$ redis-cli -h 172.30.2.21 -p 6379 cluster replicate <master-ID>
$ redis-cli -h 172.30.2.22 -p 6379 cluster replicate <master-ID>
$ redis-cli -h 172.30.2.23 -p 6379 cluster replicate <master-ID>

验证

  1. 通过以下命令查看集群中每个节点的id、角色、ip、port、插槽范围等信息。
$ redis-cli -h 172.30.2.11 -p 6379 cluster nodes
  1. 往集群存入4个键值。
$ redis-cli -c -h 172.30.2.11 -p 6379 set key1 value1
$ redis-cli -c -h 172.30.2.11 -p 6379 set key2 value2
$ redis-cli -c -h 172.30.2.11 -p 6379 set key3 value3
$ redis-cli -c -h 172.30.2.11 -p 6379 set key4 value4
  1. 查看每个主节点现有的键值,会发现每个节点只有一部分键值。
$ redis-cli -h 172.30.2.11 -p 6379 --scan
$ redis-cli -h 172.30.2.12 -p 6379 --scan
$ redis-cli -h 172.30.2.13 -p 6379 --scan