# 7.1 搭建基于主从复制模式的集群

在主从复制模式的集群里,主节点一般是一个,从节点一般是两个或多个,写入主节点的数据会被复制到从节点上,这样一旦主节点出现故障,应用系统就能切换到从节点去读写数据,提升系统的可用性.再采用主从复制模式里默认的读写分离机制,就能提升系统的缓存读写性能.对性能和实时性不高的系统而言,主从复制模式足以满足一般的性能和安全性方面的需求.

## 7.1.1 主从复制模式概述

在实际应用中,如果有相应的设置,在向一台Redis服务器里写数据后,这个数据可以复制到另外一台(或多台)Redis服务器,这里数据源服务器叫主服务器(Master Server),复制数据目的地所在的服务器叫从服务器(Slave Server)

这种主从复制模式能带来2个好处:

1. 可以把写操作集中到主服务器上,把读操作集中到从服务器上,以提升读写性能
2. 由于出现了数据备份,因此能提升数据的安全性,比如当主Redis服务器失效后,能很快切换到从服务器上读数据

![Redis主从结构](https://995259327-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQOXdfoFsRNCGz5Gl5IyE%2Fuploads%2Fgit-blob-5480c8dd8aac06d4702e5bf040ddb90620c0fe0a%2FRedis%E4%B8%BB%E4%BB%8E%E7%BB%93%E6%9E%84.jpg?alt=media)

主从复制模式的要点:

1. 一个主服务器可以带一个或多个从服务器,从服务器可以再带从服务器,但在复制数据时只能把主服务器的数据复制到从服务器上,反之不能
2. 一台从服务器只能跟随一台主服务器,不能出现一从多主的模式
3. 在Redis2.8以后的版本里,采用异步的复制模式,即进行主从复制时不会影响主服务器上的读写数据操作

## 7.1.2 用命令搭建主从集群

本例中使用Docker搭建1主2从模式的集群.配置主从关系时,需要在从节点上使用`SLAVEOF`命令

* step1. 创建主节点容器

```
(base) root@yuanhong ~ % docker run -itd --name redis-master -p 6379:6379 redis:latest
402d3999682b4c6a173d85a79eb5dfa4d491992df99730e03559f8c9f713a4db
```

```
(base) root@yuanhong ~ % docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                    NAMES
402d3999682b   redis:latest   "docker-entrypoint.s…"   4 seconds ago   Up 2 seconds   0.0.0.0:6379->6379/tcp   redis-master
```

* step2. 创建从节点1容器

```
(base) root@yuanhong ~ % docker run -itd --name redis-slave1 -p 6380:6380 redis:latest
8ab67ec497a9ab1a587e17e1509f99e96b58e381d39acfa82f3ba421fd7d761b
```

```
(base) root@yuanhong ~ % docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                              NAMES
8ab67ec497a9   redis:latest   "docker-entrypoint.s…"   33 seconds ago   Up 32 seconds   6379/tcp, 0.0.0.0:6380->6380/tcp   redis-slave1
402d3999682b   redis:latest   "docker-entrypoint.s…"   5 minutes ago    Up 5 minutes    0.0.0.0:6379->6379/tcp             redis-master
```

* step3. 检查主节点容器的IP地址

```
(base) root@yuanhong ~ % docker inspect redis-master|grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.2",
                    "IPAddress": "172.17.0.2",
```

可以看到,主节点容器的IP地址为`172.17.0.2`

* step4. 进入主节点容器,检查当前的主从模式状态

```
(base) root@yuanhong ~ % docker exec -it redis-master /bin/bash
root@402d3999682b:/data# redis-cli
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:86fe8508c48c7f521d6b057d36b6d8ef757f795b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
```

其中:

* `role:master`:表示该节点的角色为主服务器
* `connected_slaves:0`:表示当前该主服务器没有携带从服务器
* step5. 进入从节点容器,检查当前主从模式状态

```
(base) root@yuanhong ~ % docker exec -it redis-slave1 /bin/bash
root@8ab67ec497a9:/data# redis-cli
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:da60f945afea248b514978a4ad6d0fac764de983
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
```

* step6. 设置从节点容器为从服务器

在redis-slave1容器中执行如下命令:

```
127.0.0.1:6379> SLAVEOF 172.17.0.2 6379
OK
```

检查从节点容器的主从状态:

```
# Replication
role:slave
master_host:172.17.0.2
master_port:6379
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_read_repl_offset:56
slave_repl_offset:56
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:f23930d90daac10a59515702826293135f94c133
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:56
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:56
```

其中:

* `role:slave`:表示该节点的角色为从服务器
* `master_host:172.17.0.2`:主节点IP地址
* `master_port:6379`:主节点端口
* step7. 在主节点中查看主从状态

```
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:1
slave0:ip=172.17.0.3,port=6379,state=online,offset=714,lag=1
master_failover_state:no-failover
master_replid:f23930d90daac10a59515702826293135f94c133
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:728
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:728
```

* `connected_slaves:1`:可以看到,此时已经有1个从节点了
* step8. 创建从节点2容器

```
(base) root@yuanhong ~ % docker run -itd --name redis-slave2 -p 6381:6381 redis:latest
f4a1f14d506c49a9df1663dd45dfbbec9685c52ce683ec54c98bd3124c3789db
```

```
(base) root@yuanhong ~ % docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                              NAMES
f4a1f14d506c   redis:latest   "docker-entrypoint.s…"   11 seconds ago   Up 10 seconds   6379/tcp, 0.0.0.0:6381->6381/tcp   redis-slave2
8ab67ec497a9   redis:latest   "docker-entrypoint.s…"   25 minutes ago   Up 25 minutes   6379/tcp, 0.0.0.0:6380->6380/tcp   redis-slave1
402d3999682b   redis:latest   "docker-entrypoint.s…"   30 minutes ago   Up 30 minutes   0.0.0.0:6379->6379/tcp             redis-master
```

* step9. 进入从节点2容器,设置该容器为从服务器

```
(base) root@yuanhong ~ % docker exec -it redis-slave2 /bin/bash
root@f4a1f14d506c:/data# redis-cli
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:93c3dae3b5381a7a4b710163e6f33ddef085cd00
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
```

可以看到,此时该容器为主节点

```
127.0.0.1:6379> SLAVEOF 172.17.0.2 6379
OK
127.0.0.1:6379> INFO replication
# Replication
role:slave
master_host:172.17.0.2
master_port:6379
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_read_repl_offset:1344
slave_repl_offset:1344
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:f23930d90daac10a59515702826293135f94c133
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1344
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1345
repl_backlog_histlen:0
```

* step10. 在主节点容器中查看主从情况

```
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.17.0.3,port=6379,state=online,offset=1456,lag=0
slave1:ip=172.17.0.4,port=6379,state=online,offset=1456,lag=0
master_failover_state:no-failover
master_replid:f23930d90daac10a59515702826293135f94c133
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1456
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1456
```

可以看到,此时有2个从节点

## 7.1.3 通过配置搭建主从集群

* step1. 编写主节点的配置文件

```
(base) root@yuanhong section7-1 % cat master.conf 
# 指定default用户的密码 允许执行所有命令 允许访问所有key 允许访问所有频道
user default on >default_password ~* &* +@all

# 指定用户名和密码 允许执行所有命令 允许访问所有key 允许访问所有频道
user master_user on >master_password  ~* &* +@all

# 指定端口
port 6379
```

* step2. 根据配置文件启动主节点容器

启动容器:

```
(base) root@yuanhong section7-1 % docker run -itd --name redis-master -v /StudyRedisBaseOnDocker/conf/chapter7/section7-1/master.conf:/redisConf/master.conf:rw -p 6379:6379 redis:latest redis-server /redisConf/master.conf
689437cb98ce2bd563413be89529872b74421b63f73b9dcd25f7a995b084c0ea
```

检查容器状态:

```
(base) root@yuanhong section7-1 % docker ps   
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                    NAMES
689437cb98ce   redis:latest   "docker-entrypoint.s…"   12 seconds ago   Up 11 seconds   0.0.0.0:6379->6379/tcp   redis-master
```

进入容器检查用户名密码是否配置正确:

```
(base) root@yuanhong section7-1 % docker exec -it redis-master /bin/bash
root@689437cb98ce:/data# redis-cli
127.0.0.1:6379> AUTH master_user master_password
OK
127.0.0.1:6379> ACL WHOAMI
"master_user"
```

* step3. 检查主节点容器IP地址

```
(base) root@yuanhong section7-1 % docker inspect redis-master|grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.2",
                    "IPAddress": "172.17.0.2",
```

* step4. 编写从节点1的配置文件

```
(base) root@yuanhong section7-1 % cat slave_1.conf
# 指定default用户的密码 允许执行所有命令 允许访问所有key 允许访问所有频道
user default on >default_password ~* &* +@all

# 指定用户名和密码 允许执行所有命令 允许访问所有key 允许访问所有频道
user slave_1_user on >slave_1_password  ~* &* +@all

# 指定端口
port 6380

# 指定主节点IP和端口
slaveof 172.17.0.2 6379

# 指定主节点用户名
masteruser master_user

# 指定主节点密码
masterauth master_password
```

* step5. 根据配置文件启动从节点1容器

启动容器:

```
(base) root@yuanhong section7-1 % docker run -itd --name redis-slave1 -v /StudyRedisBaseOnDocker/conf/chapter7/section7-1/slave_1.conf:/redisConf/slave_1.conf:rw -p 6380:6380 redis:latest redis-server /redisConf/slave_1.conf
2e52199b473cc1a3062975fa1371d2230ebb9991154d5428577ce9f01be9cd1f
```

检查容器状态:

```
(base) root@yuanhong section7-1 % docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                              NAMES
2e52199b473c   redis:latest   "docker-entrypoint.s…"   1 second ago     Up 1 second     6379/tcp, 0.0.0.0:6380->6380/tcp   redis-slave1
689437cb98ce   redis:latest   "docker-entrypoint.s…"   13 minutes ago   Up 13 minutes   0.0.0.0:6379->6379/tcp             redis-master
```

进入容器检查用户名密码是否配置正确:

```
(base) root@yuanhong section7-1 % docker exec -it redis-slave1 /bin/bash
root@2e52199b473c:/data# redis-cli -h 127.0.0.1 -p 6380
127.0.0.1:6380> AUTH slave_1_user slave_1_password
OK
127.0.0.1:6380> ACL WHOAMI
"slave_1_user"
127.0.0.1:6380> 
```

* step6. 在从节点1容器中确认主从状态

```
127.0.0.1:6380> INFO replication
# Replication
role:slave
master_host:172.17.0.2
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_read_repl_offset:322
slave_repl_offset:322
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:523bf7a915b56cfe75b1b1032b312e484ccbc0a6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:322
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:322
```

* step7. 在主节点容器中确认主从状态

```
root@689437cb98ce:/data# redis-cli
127.0.0.1:6379> AUTH master_user master_password
OK
127.0.0.1:6379> ACL WHOAMI
"master_user"
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:1
slave0:ip=172.17.0.3,port=6380,state=online,offset=392,lag=0
master_failover_state:no-failover
master_replid:523bf7a915b56cfe75b1b1032b312e484ccbc0a6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:392
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:392
```

* step8. 编写从节点2的配置文件

```
(base) root@yuanhong section7-1 % cat slave_2.conf 
# 指定default用户的密码 允许执行所有命令 允许访问所有key 允许访问所有频道
user default on >default_password ~* &* +@all

# 指定用户名和密码 允许执行所有命令 允许访问所有key 允许访问所有频道
user slave_2_user on >slave_2_password  ~* &* +@all

# 指定端口
port 6381

# 指定主节点IP和端口
slaveof 172.17.0.2 6379

# 指定主节点用户名
masteruser master_user

# 指定主节点密码
masterauth master_password
```

* step9. 根据配置文件启动从节点2容器

启动容器:

```
(base) root@yuanhong section7-1 % docker run -itd --name redis-slave2 -v /StudyRedisBaseOnDocker/conf/chapter7/section7-1/slave_2.conf:/redisConf/slave2.conf:rw -p 6381:6381 redis:latest redis-server /redisConf/slave2.conf
bffc87be1f2fb321aadb07b520d452d2c980fa4d94d383bda19f187ff46df578
```

检查容器状态:

```
(base) root@yuanhong section7-1 % docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                              NAMES
bffc87be1f2f   redis:latest   "docker-entrypoint.s…"   10 seconds ago   Up 10 seconds   6379/tcp, 0.0.0.0:6381->6381/tcp   redis-slave2
2e52199b473c   redis:latest   "docker-entrypoint.s…"   12 minutes ago   Up 12 minutes   6379/tcp, 0.0.0.0:6380->6380/tcp   redis-slave1
689437cb98ce   redis:latest   "docker-entrypoint.s…"   26 minutes ago   Up 26 minutes   0.0.0.0:6379->6379/tcp             redis-master
```

进入容器检查用户名密码是否配置正确:

```
(base) root@yuanhong section7-1 % docker exec -it redis-slave2 /bin/bash
root@bffc87be1f2f:/data# redis-cli -h 127.0.0.1 -p 6381
127.0.0.1:6381> AUTH slave_2_user slave_2_password
OK
127.0.0.1:6381> ACL WHOAMI
"slave_2_user"
```

* step10. 在从节点2容器中确认主从状态

```
127.0.0.1:6381> INFO replication
# Replication
role:slave
master_host:172.17.0.2
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_read_repl_offset:1232
slave_repl_offset:1232
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:523bf7a915b56cfe75b1b1032b312e484ccbc0a6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1232
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1051
repl_backlog_histlen:182
```

* step11. 在主节点容器中确认主从状态

```
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.17.0.3,port=6380,state=online,offset=1260,lag=0
slave1:ip=172.17.0.4,port=6381,state=online,offset=1260,lag=0
master_failover_state:no-failover
master_replid:523bf7a915b56cfe75b1b1032b312e484ccbc0a6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1260
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1260
```

可以看到,此时主节点上可以看到2个从节点的信息

* step12. 测试

主节点上写入:

```
127.0.0.1:6379> SET age 18
OK
```

分别在2个从节点上读取:

```
127.0.0.1:6380> GET age
"18"
```

```
127.0.0.1:6381> GET age
"18"
```

## 7.1.4 配置读写分离效果

在2个从节点上查看配置主从状态:

```
127.0.0.1:6381> INFO replication
# Replication
role:slave
master_host:172.17.0.2
master_port:6379
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_read_repl_offset:2419
slave_repl_offset:2419
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:523bf7a915b56cfe75b1b1032b312e484ccbc0a6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:2419
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1051
repl_backlog_histlen:1369
```

```
127.0.0.1:6380> INFO replication
# Replication
role:slave
master_host:172.17.0.2
master_port:6379
master_link_status:up
master_last_io_seconds_ago:10
master_sync_in_progress:0
slave_read_repl_offset:2587
slave_repl_offset:2587
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:523bf7a915b56cfe75b1b1032b312e484ccbc0a6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:2587
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:2587
```

其中:

* `slave_read_only:1`:表示从服务器为只读

修改slave2节点为可读可写:

* step1. 停止redis-slave2容器

```
(base) root@yuanhong section7-1 % docker stop redis-slave2
redis-slave2
```

* step2. 修改redis-slave2的配置文件

```
(base) root@yuanhong section7-1 % cat slave_2.conf                      
# 指定default用户的密码 允许执行所有命令 允许访问所有key 允许访问所有频道
user default on >default_password ~* &* +@all

# 指定用户名和密码 允许执行所有命令 允许访问所有key 允许访问所有频道
user slave_2_user on >slave_2_password  ~* &* +@all

# 指定端口
port 6381

# 指定主节点IP和端口
slaveof 172.17.0.2 6379

# 指定主节点用户名
masteruser master_user

# 指定主节点密码
masterauth master_password

# 允许从节点执行写入操作
slave-read-only no
```

* step3. 启动redis-slave2容器

```
(base) root@yuanhong section7-1 % docker start redis-slave2             
redis-slave2
```

* step4. 进入redis-slave2容器并尝试写入

```
(base) root@yuanhong section7-1 % docker exec -it redis-slave2 /bin/bash
root@bffc87be1f2f:/data# redis-cli -h 127.0.0.1 -p 6381
127.0.0.1:6381> AUTH slave_2_user slave_2_password
OK
127.0.0.1:6381> GET age
"18"
127.0.0.1:6381> SET name Peter
OK
```

可以看到,此时已经可以在从节点2上写入了

在主节点和从节点1上读取:

```
127.0.0.1:6379> GET name
(nil)
```

```
127.0.0.1:6380> GET name
(nil)
```

因此,需要注意的是:

在Redis主从集群模式下,从节点(Slave)默认是只读的.当你将从节点设置为可写入模式(`slave-read-only no`),这意味着客户端可以直接写入到这个从节点.然而，这种配置并不是常规的使用模式,并且有其特定的行为和限制.

以下是关于在从节点上写入时发生的事情的解释:

1. **写入不会同步到主节点或其他从节点**:当你直接在从节点上进行写入操作时,这些写入不会被传播到主节点或其他从节点.这是因为在Redis的复制模型中,只有主节点才会将其写入操作传播到从节点
2. **数据可能丢失**:由于从节点上的写入不会同步到其他节点,因此如果这个从节点出现故障或重新启动,这些写入可能会丢失.因为当从节点重新连接到主节点时,它会从主节点获取最新的数据快照,从而丢弃任何在从节点上直接进行的写入
3. **可能导致数据不一致**:由于从节点上的写入不会传播,这意味着集群中的不同节点可能会有不同的数据,从而导致数据不一致

基于上述原因,允许直接在从节点上进行写入(将`slave-read-only`设置为`no`)通常不是推荐的做法.这种配置可能会导致数据丢失、数据不一致,并且与Redis的传统复制模型不符

如果您需要一个可写的分布式数据存储解决方案,您可能需要考虑使用Redis Cluster,它支持多个主节点,并可以在节点之间自动分片数据

## 7.1.5 用心跳机制提高主从复制的可靠性

在Redis主从复制模式里,如果主从服务器之间有数据同步的情况,那么从服务器会默认以一秒一次的频率向主服务器发送`REPLCONF ACK`命令,以此来确保二者间连接通畅.这种用定时交互命令来确保连接的机制叫做"心跳"机制.

在主节点上执行`INFO replication`命令,可以看到从节点的心跳情况:

```
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.17.0.3,port=6380,state=online,offset=7137,lag=1
slave1:ip=172.17.0.4,port=6381,state=online,offset=7137,lag=1
master_failover_state:no-failover
master_replid:523bf7a915b56cfe75b1b1032b312e484ccbc0a6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:7137
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:7137
```

其中:

```
slave0:ip=172.17.0.3,port=6380,state=online,offset=7137,lag=1
slave1:ip=172.17.0.4,port=6381,state=online,offset=7137,lag=1
```

此处的`lag`表示的就是从节点与主节点之间的延迟(单位:秒)

在Redis中,`min-slaves-to-write`参数用于指定主节点需要多少个从节点(slaves)处于已同步状态(即与主节点的数据差异小于`min-slaves-max-lag`所指定的秒数),才允许写入操作

参数的详细含义如下:

* `min-slaves-to-write`:这是一个整数值,指定了主节点要求处于已同步状态的从节点数量,才允许执行写入操作.如果已同步的从节点数量少于此值,主节点将拒绝所有的写入命令并返回一个错误
* `min-slaves-max-lag`:与`min-slaves-to-write`配合使用,它定义了一个从节点被认为是已同步的最大延迟值(单位:秒).如果从节点的延迟大于此值,它将被认为是未同步的

这两个参数的目的是在某些场景下增加数据的持久性和可用性.例如,如果你有一个Redis设置,其中数据的可用性和不丢失写入操作是关键要求,那么你可能希望确保至少有一定数量的从节点已经接收和确认了最近的写入,才继续更多的写入

但是,请注意,这些设置可能会增加写入操作的延迟,尤其是在网络不稳定或从节点经常出现延迟的情况下.在配置这些参数之前,你应该仔细考虑其影响,并根据你的具体需求进行测试

在主节点的配置文件中增加配置心跳的参数:

```
(base) root@yuanhong section7-1 % cat master.conf 
# 指定default用户的密码 允许执行所有命令 允许访问所有key 允许访问所有频道
user default on >default_password ~* &* +@all

# 指定用户名和密码 允许执行所有命令 允许访问所有key 允许访问所有频道
user master_user on >master_password  ~* &* +@all

# 指定端口
port 6379

# 至少有2个从节点处于已同步状态时才允许执行写入操作
min-slaves-to-write 2

# 从节点延迟超过15秒即判定为未同步
min-slaves-max-lag 15
```

重新启动主节点容器:

```
(base) root@yuanhong section7-1 % docker start redis-master
redis-master
```

## 7.1.6 用偏移量检查数据是否一致

在主节点中查看偏移量:

```
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:2
min_slaves_good_slaves:2
slave0:ip=172.17.0.3,port=6381,state=online,offset=644,lag=0
slave1:ip=172.17.0.4,port=6380,state=online,offset=644,lag=0
master_failover_state:no-failover
master_replid:46d5db13c64599bf1bae2d4c276229e00cc78a46
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:644
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:644
```

其中:

* `master_repl_offset`:表示主节点向从节点发送的字节数

在从节点中查看偏移量:

```
127.0.0.1:6380> INFO replication
# Replication
role:slave
master_host:172.17.0.2
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_read_repl_offset:644
slave_repl_offset:644
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:46d5db13c64599bf1bae2d4c276229e00cc78a46
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:644
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:644
```

```
127.0.0.1:6381> INFO replication
# Replication
role:slave
master_host:172.17.0.2
master_port:6379
master_link_status:up
master_last_io_seconds_ago:9
master_sync_in_progress:0
slave_read_repl_offset:644
slave_repl_offset:644
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:46d5db13c64599bf1bae2d4c276229e00cc78a46
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:644
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:644
```

其中:

* `slave_repl_offset`:表示从节点从主节点中接收到的字节数.若和主节点的`master_repl_offset`一致,说明主从服务器之间的数据是同步的

在主节点上写入1条数据:

```
127.0.0.1:6379> SET val 1
OK
```

再观察主节点的同步信息:

```
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:2
min_slaves_good_slaves:2
slave0:ip=172.17.0.3,port=6381,state=online,offset=906,lag=0
slave1:ip=172.17.0.4,port=6380,state=online,offset=906,lag=0
master_failover_state:no-failover
master_replid:46d5db13c64599bf1bae2d4c276229e00cc78a46
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:906
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:906
```

观察从节点的同步信息:

```
127.0.0.1:6380> INFO replication
# Replication
role:slave
master_host:172.17.0.2
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_read_repl_offset:906
slave_repl_offset:906
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:46d5db13c64599bf1bae2d4c276229e00cc78a46
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:906
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:906
```

```
127.0.0.1:6381> INFO replication
# Replication
role:slave
master_host:172.17.0.2
master_port:6379
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_read_repl_offset:906
slave_repl_offset:906
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:46d5db13c64599bf1bae2d4c276229e00cc78a46
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:906
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:906
```

此时从节点从主节点处读取到的字节数等于主节点向从节点发送的字节数.说明刚刚的写操作同步成功了.若同步出现问题,则可以通过`master_repl_offset`和`slave_repl_offset`参数值进行排查
