Redis主从复制

首先需要设置一个master节点,两个slave节点,然后修改各自的redis.conf,让两个slave节点指向主master节点:

(1)设置master节点的port=7000,bind=0.0.0.0

(2)分别设置两个slave节点的port为7001和7002,同时bind=0.0.0.0

(3)在slave节点上开启主从复制——修改Replicof选项:

1
replicaof  192.168.193.129  7000

(4)定位到redis-server安装目录(ubuntu使用apt install默认安装在/usr/bin位置),然后使用以下命令:

1
2
3
./redis-server  /home/cat/master/redis.conf #启动主master节点
./redis-server /home/cat/slave1/redis.conf #启动slave1节点
./redis-server /home/cat/slave2/redis.conf #启动slave2节点

结果如下:tsVFa.png

也可以在客户端redis-cli下登录master节点查看结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.193.129,port=7001,state=online,offset=42,lag=1
slave1:ip=192.168.193.129,port=7002,state=online,offset=42,lag=1
master_replid:8eae2408ed586d2d9d7576610dae13f16de3de24
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:42

在主节点设置两个key后,可以看出slave节点上也会自动同步相应的数据:

tsDMp.png

注意:从节点不负责请求处理,只负责同步主节点数据,即只读,因此默认情况下在slave节点上修改数据是不被允许的

1
2
127.0.0.1:7002> set age 24
(error) READONLY You can't write against a read only replica.

且一旦master节点宕机,从节点也不能替代成为新的主节点(会一直报错):

1
2
3
29017:C 03 Jan 2023 22:50:58.074 # Failed opening the RDB file dump.rdb (in server root dir /usr/bin) for saving: Permission denied
28737:S 03 Jan 2023 22:50:58.174 # Background saving error

从节点仅作备份可用性不高,因此接下来介绍redis提供的哨兵机制:

哨兵机制

Sentinel(哨兵)是Redis 的高可用性解决方案:由一个或多个Sentinel实例组成的Sentinel 系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。简单的说哨兵就是带有自动故障转移功能的主从架构。即当一个master节点的redis服务挂掉后,sentinel机制会发现, 并从剩余的slave节点的redis服务中, 选举出一个slave, 来充当新的master节点,并让其他存活slave节点指向新的master节点,当原master节点复活后自动降级为slave节点同样指向新master节点。

修改/etc/redis目录下的sentinel.conf配置文件(该配置文件对应默认端口号为6379的主节点),编辑内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
port 26379 # sentinel节点端口
daemonize yes # 守护线程
bind 0.0.0.0
sentinel monitor mainmaster 192.168.193.129 6379 2 #master节点别名 路径 端口号 过半数投票数
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
# master-name 可以自己命名的主节点名字
# ip为起始设置为master节点的ip地址,port同理
# quorum 判断主节点失败至少需要quorum个sentinel节点同意
sentinel auth-pass mainmaster emei268 # master节点密码
sentinel down-after-milliseconds mainmaster 10000 # 下线时间,单位毫秒, 即超过10s则认为不可达
sentinel parallel-syncs mainmaster 1 # 故障转移后,发起复制的节点限制个数
sentinel failover-timeout mainmaster 60000 # 故障转移超时时间



查看redis服务,可以发现第一个哨兵已经运行在26379端口了:

1
2
3
4
5
6
7
8
(base) cat@cat-virtual-machine:~/桌面$ ps -ef | grep redis
root 4204 1048 0 17:46 ? 00:00:06 redis-server 0.0.0.0:6379
cat 4239 4219 0 17:47 pts/2 00:00:00 redis-cli
cat 4310 4276 0 17:47 pts/3 00:00:05 redis-server 0.0.0.0:7001
cat 4344 4326 0 17:48 pts/4 00:00:05 redis-server 0.0.0.0:7002
root 4626 1048 0 18:32 ? 00:00:00 redis-sentinel 0.0.0.0:26379 [sentinel]
cat 4632 4370 0 18:32 pts/5 00:00:00 grep --color=auto redis

1
2
3
4983:X 04 Jan 2023 18:56:43.168 # Sentinel ID is aa3d45daf2d6576fdf3d61a605cc2ca860930c32
4983:X 04 Jan 2023 18:56:43.168 # +monitor master mainmaster 192.168.193.129 6379 quorum 1

且该哨兵所对应的sentinel.conf内容已经被自动修改:

1
2
3
4
5
6
7
8
9
10
11
sentinel myid aa3d45daf2d6576fdf3d61a605cc2ca860930c32
# Generated by CONFIG REWRITE
protected-mode no
port 26379
user default on nopass ~* +@all
dir "/home/cat/\xe6\xa1\x8c\xe9\x9d\xa2"
sentinel deny-scripts-reconfig yes
sentinel monitor mainmaster 192.168.193.129 6379 1
sentinel config-epoch mainmaster 0
sentinel leader-epoch mainmaster 1
sentinel current-epoch 1

切换主节点

配置哨兵前,如果从节点想要修改会提示无权限(仅有复制权限):

1
2
3
4
127.0.0.1:7001> set country france
(error) READONLY You can't write against a read only replica.
127.0.0.1:7001>

配置哨兵后,让master节点宕机—执行kill -9 4204命令,然后slave1节点上执行数据修改操作会发现操作成功(即该节点已经成为新的主节点):

1
2
127.0.0.1:7001> set age 24
OK

Redis集群

前面介绍了redis主从复制架构,知道了该工作模式下提供多台redis服务,选择其中的一台作为master节点向外提供读写服务,剩下的作为slave节点从master节点复制数据,只向外提供读服务。随后引入了Redis哨兵机制,实现了对所有redis节点的监控和master的自动故障转移。一般情况下来说,上面两种方式结合起来使用已经可以满足大部分的redis高可用场景,但它有一个很明显的缺点:只有一台master节点向外提供写服务,其他的slave节点只能提供读服务。所以,当写操作并发量很大的,无法缓解写操作的压力。针对这种场景,Redis在3.0版本中引入了Redis集群的部署架构

集群特性

(1) Redis集群是一个提供在多个Redis节点之间共享数据的程序集。它并不像Redis主从复制模式那样只提供一个master节点提供写服务,而是会提供多个master节点提供写服务,每个master节点中存储的数据都不一样,这些数据通过数据分片的方式被自动分割到不同的master节点上

(2)为了保证集群的高可用,每个master节点下面还需要添加至少1个slave节点,这样当某个master节点发生故障后,可以从它的slave节点中选举一个作为新的master节点继续提供服务(哨兵机制)。不过当某个master节点和它下面所有的slave节点都发生故障时,整个集群就不可用了

(3)节点的失效fail是通过集群中超过半数节点检测失效时才生效,且客户端与redis集群中节点直连,不需要中间代理proxy

(4)哈希槽:Redis集群中引入了哈希槽的概念,Redis集群有16384个哈希槽,进行set操作时,每个key会通过CRC16校验后再对16384取模来决定放置在哪个槽,搭建Redis集群时会给集群中每个master节点分配一部分哈希槽(因此redis集群中最大节点数为16384个)。比如当前集群有3个master节点,master1节点包含05500号哈希槽,master2节点包含550111000号哈希槽,master3节点包含11001~16384号哈希槽,当我们执行“set key value”时,假如 CRC16(key) % 16384 = 777,那么这个key就会被分配到master1节点上。某个物理节点由于故障进行主从切换时不涉及到哈希槽的重分配,而如果动态新增master物理节点时则会对哈希槽进行重新分配,且移动槽的过程中也会移动value

(5)集群中节点的通信:既然Redis集群中的数据是通过哈希槽的方式分开存储的,那么集群中每个节点都需要知道其他所有节点的状态信息,包括当前集群状态、集群中各节点负责的哈希槽、集群中各节点的master-slave状态、集群中各节点的存活状态等。Redis集群中,节点之间通过建立TCP连接,使用gossip协议来传播集群的信息,所谓gossip协议,指的是一种消息传播的机制,类似人们传递八卦消息似的,一传十,十传百,直至所有人都知道这条八卦内容。Redis集群中各节点之间传递消息就是基于gossip协议,最终达到所有节点都会知道整个集群完整的信息。gossip协议有4种常用的消息类型:PING、PONG、MEET、FAIL

集群搭建

搭建redis集群至少需要6个节点(3个master及其各自附属的slave节点)

  1. 搭建集群需要先安装相应依赖:

    1
    2
    apt-get install -y ruby
    gem install redis
  2. 新建7个目录保存各个节点,然后修改各个节点的配置文件(以/cluster/8000/redis.conf为例):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    42 daemonize yes            #以后台守护方式运行
    46 pidfile "/var/run/redis/redis_8000.pid" #pid文件位置
    50 port 8000 #监听端口
    509 appendonly yes #以追加方式存储数据
    513 appendfilename "appendonly8000.aof" #存储数据的文件
    637 cluster-enabled yes #开启集群模式
    645 cluster-config-file nodes-8000.conf #节点文件
    651 cluster-node-timeout 5000 #节点超时时间

  3. 然后启动这7个节点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    root@cat-virtual-machine:/home/cat/cluster# ps -ef|grep redis
    root 6552 1048 0 21:27 ? 00:00:00 redis-server 0.0.0.0:8000 [cluster]
    root 6558 1048 0 21:27 ? 00:00:00 redis-server 0.0.0.0:8001 [cluster]
    root 6564 1048 0 21:27 ? 00:00:00 redis-server 0.0.0.0:8002 [cluster]
    root 6573 1048 0 21:27 ? 00:00:00 redis-server 0.0.0.0:8003 [cluster]
    root 6579 1048 0 21:27 ? 00:00:00 redis-server 0.0.0.0:8004 [cluster]
    root 6585 1048 0 21:27 ? 00:00:00 redis-server 0.0.0.0:8005 [cluster]
    root 6591 1048 0 21:27 ? 00:00:00 redis-server 0.0.0.0:8006 [cluster]
    root 6598 5508 0 21:27 pts/3 00:00:00 grep --color=auto redis

  4. 将redis-trib.rb复制到 /usr/bin目录下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    cp /usr/share/doc/redis-tools/examples/redis-trib.rb /usr/bin/redis-trib.rb

    redis-trib.rb create --replicas 1 192.168.193.129:8000 192.168.193.129:8001 192.168.193.129:8002 192.168.193.129:8003 192.168.193.129:8004 192.168.193.129:8005 (旧版本redis)
    #replicas参数后面的1表示每个master节点有一个slave从节点, 且默认前三个地址对应的节点为主节点,后三个为从节点

    ps:新版本redis使用以下命令创建集群:redis-cli --cluster SUBCOMMAND [ARGUMENTS] [OPTIONS]
    例如 redis-cli --cluster create 192.168.193.129:8000 192.168.193.129:8001 192.168.193.129:8002 192.168.193.129:8003 192.168.193.129:8004 192.168.193.129:8005 --cluster-replicas 1 -a emei268


    在执行完上述命令后,会输出以下结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    root@cat-virtual-machine:/home/cat/cluster# redis-cli --cluster create  192.168.193.129:8000  192.168.193.129:8001  192.168.193.129:8002  192.168.193.129:8003 192.168.193.129:8004  192.168.193.129:8005  --cluster-replicas 1  -a emei268
    Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
    >>> 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.193.129:8004 to 192.168.193.129:8000
    Adding replica 192.168.193.129:8005 to 192.168.193.129:8001
    Adding replica 192.168.193.129:8003 to 192.168.193.129:8002
    >>> Trying to optimize slaves allocation for anti-affinity
    [WARNING] Some slaves are in the same host as their master
    M: 208dc519f5a74aae11915d1511dd7ff3624ffe7a 192.168.193.129:8000
    slots:[0-5460] (5461 slots) master
    M: ea5afe7e4e9468ee336f3da99dd67e7e7d5f9840 192.168.193.129:8001
    slots:[5461-10922] (5462 slots) master
    M: b5fa4e4f6c7c145393f4c968c9419d9298fa087d 192.168.193.129:8002
    slots:[10923-16383] (5461 slots) master
    S: 1793e4730091fcbb1c3c919b80aa372f0d5b75fe 192.168.193.129:8003
    replicates 208dc519f5a74aae11915d1511dd7ff3624ffe7a
    S: a200f785d7e51965a548f324b24caf0a2192d609 192.168.193.129:8004
    replicates ea5afe7e4e9468ee336f3da99dd67e7e7d5f9840
    S: dd107b4890ef4f51b7a284395b9b97c9d4ca4a40 192.168.193.129:8005
    replicates b5fa4e4f6c7c145393f4c968c9419d9298fa087d

输出以上结果说明集群已经创建成功啦

集群测试

1
2
127.0.0.1:8000> set name lingard
-> Redirected to slot [5798] located at 192.168.193.129:8001

往集群里放入值时会先做重定向(crc16+hash映射槽),同样取值的时候也会做redirect操作

1
python segment/predict.py --weights yolov7-seg.pt --source "videopath.mp4"