本文最后更新于 319 天前,其中的信息可能已经过时,如有错误请发送邮件到wuxianglongblog@163.com
Redis主从复制的环境搭建
一.Redis的作者antirez将"master-slave"架构的描述改为"master-replica"
相信在座各位的开发者都不会对Redis的主从模式(master-slave)感到陌生。Redis中的主从模式事实上也是源自MySQL中同名的一个概念,是同步数据的一种手段。在这样的场景下,master-slave本来是一个不带任何政治感情色彩的词语。
然而有很多开发者却不这样认为,他们在阅读 Redis 5.0 RC5 的变更日志后发现,Redis仍然在使用"master"和"slave"术语来在Redis的复制中区分不同的角色,他们对此感到失望,并希望Redis作者能修改Redis中master-slave架构的描述。
Redis的作者antirez发文表示他很抱歉master-slave这个描述让许多人感到失望,但他不认为这个特定于上下文环境的术语具有侵犯性。所以他在数据库中使用master-slave当然不是以任何方式暗示"奴隶制度"。
短期内的变化:
(1)将"master-slave"架构的描述改为"master-replica";
(2)为"SLAVEOF"提供别名"REPLICAOF",所以仍然可以使用 SLAVEOF,但多了一个选项;
(3)保持继续使用"slave"来对"INFO"和"ROLE"进行回应,现在目前看来,这仍然是一个重大的破坏性变更;
长期变化:
(1)编写一个 INFO 的替代品
(2)在内部替换很多东西,因为技术原因,如果作了改动,许多PR也会无法应用,所以必须在某些地方进行大变动
二.master-replica架构原理
1.master-replica架构工作原理
如下图所示,是Redis的工作原理,其对应的文字说明如下:
1>.replica库通过"SLAVEOF 172.200.1.201 6379"命令连接master库并发送"SYNC"给master库;
2>.master库收到"SYNC",会立即触发"BGSAVE"后台保存RDB快照,并将RDB快照发送给replica库;
3>.replica库接收后会应用RDB快照;
4>.master库会陆续将中间产生的新的操作保存并发送给replica库;
5>.到此我们master复制集就正常工作了;
6>.再此以后,master库只要发生新的操作,都会以命令传播的形式自动发送给replica库;
7>.所有复制相关信息,从info信息中都可以查到,即使重启任何节点,他的master-replica关系依然都在;
8>.如果发生master-replica关系断开时,replica库数据没有任何损坏,在下次重连之后,replica库发送"PSYNC"给master库;
9>.master库只会将replica库缺失部分的数据同步给replica库应用,达到快速恢复master-replica架构的目的;
温馨提示:(可能面试时会问到)
(1)在Redis 2.8版本之前,如果master-replica架构中replica库宕机将会再次触发"SYNC",从而全量拷贝主库的数据,这样效率明显很低。于是在Redis 2.8版本之后就新增了"PSYNC"指令以实现可以理解为"断点续传"的功能。
(2)生产环境中,建议开启master库持久化功能,避免因主库意外重启而导致缓存丢失。举个例子,master库右1w条数据,replica库在某一时刻仅有9k数据,还有1k数据未来得及同步master库时,master库意外重启导致KEY为0,此时replica库同步master库数据会将replica库已有的9k缓存数据删除以达到和master库同步的目的。
2.主从数据一致性保证,类似于MySQL的半同步复制,了解即可。
min-slaves-to-write 1
min-slaves-max-lag 3
参数说明:
min-slaves-to-write:
指定最小的的从库写入节点。
min-slaves-max-lag:
指定从库最小的延迟时间。
温馨提示:
上述两个参数均是为了保证数据写入的一致性,和MySQL的半同步复制有异曲同工之妙。
综上所述,Redis的master-replica架构也不能100%保证数据的一致性。因为一旦"min-slaves-to-write"和"min-slaves-max-lag"就会都触发了,则由同步复制转换为异步复制。
3.面试题
(1)PSYNC作用在哪?
答:在Redis 2.8版本之后就新增了"PSYNC"指令以实现可以理解为"断点续传"的功能。不会像"SYNC"那样同步全部的数据,而是差异的部分进行数据同步。
(2)主库是否要开启持久化?
答:如果不开有可能,主库重启操作,造成所有主从数据丢失!
三.准备3个Redis实例部署master-replica架构实战
1.为各个Redis实例创建数据和配置文件目录
(1)创建Redis的数据存储目录
[root@redis201.oldboyedu.com ~]# mkdir -pv /oldboyedu/data/redis1637{0..2}
mkdir: 已创建目录 "/oldboyedu/data/redis16370"
mkdir: 已创建目录 "/oldboyedu/data/redis16371"
mkdir: 已创建目录 "/oldboyedu/data/redis16372"
[root@redis201.oldboyedu.com ~]#
(2)创建Redis的配置文件存储目录
[root@redis201.oldboyedu.com ~]# mkdir -pv /oldboyedu/softwares/redis1637{0..2}
mkdir: 已创建目录 "/oldboyedu/softwares/redis16370"
mkdir: 已创建目录 "/oldboyedu/softwares/redis16371"
mkdir: 已创建目录 "/oldboyedu/softwares/redis16372"
[root@redis201.oldboyedu.com ~]#
2.为各个Redis实例创建配置文件
(1)为"redis16370"实例创建配置文件
[root@redis201.oldboyedu.com ~]# cat > /oldboyedu/softwares/redis16370/redis.conf <<EOF
> port 16370
> daemonize yes
> pidfile /oldboyedu/data/redis16370/redis.pid
> loglevel notice
> logfile /oldboyedu/data/redis16370/redis.log
> dbfilename dump.rdb
> dir /oldboyedu/data/redis16370
> bind 172.200.1.201 127.0.0.1
> requirepass oldboyedu
> masterauth oldboyedu
> # 配置RDB持久化策略
> save 900 1
> save 300 10
> save 60 10000
> # 配置AOF持久化
> appendonly yes
> appendfsync everysec
> EOF
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# cat /oldboyedu/softwares/redis16370/redis.conf
port 16370
daemonize yes
pidfile /oldboyedu/data/redis16370/redis.pid
loglevel notice
logfile /oldboyedu/data/redis16370/redis.log
dbfilename dump.rdb
dir /oldboyedu/data/redis16370
requirepass oldboyedu
masterauth oldboyedu
port 16370
daemonize yes
pidfile /oldboyedu/data/redis16370/redis.pid
loglevel notice
logfile /oldboyedu/data/redis16370/redis.log
dbfilename dump.rdb
dir /oldboyedu/data/redis16370
bind 172.200.1.201 127.0.0.1
requirepass oldboyedu
masterauth oldboyedu
# 配置RDB持久化策略
save 900 1
save 300 10
save 60 10000
# 配置AOF持久化
appendonly yes
appendfsync everysec
[root@redis201.oldboyedu.com ~]#
(2)为"redis16371"实例创建配置文件
[root@redis201.oldboyedu.com ~]# cat > /oldboyedu/softwares/redis16371/redis.conf <<EOF
> port 16371
> daemonize yes
> pidfile /oldboyedu/data/redis16371/redis.pid
> loglevel notice
> logfile /oldboyedu/data/redis16371/redis.log
> dbfilename dump.rdb
> dir /oldboyedu/data/redis16371
> bind 172.200.1.201 127.0.0.1
> requirepass oldboyedu
> masterauth oldboyedu
> # 配置RDB持久化策略
> save 900 1
> save 300 10
> save 60 10000
> # 配置AOF持久化
> appendonly yes
> appendfsync everysec
> EOF
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# cat /oldboyedu/softwares/redis16371/redis.conf
port 16371
daemonize yes
pidfile /oldboyedu/data/redis16371/redis.pid
loglevel notice
logfile /oldboyedu/data/redis16371/redis.log
dbfilename dump.rdb
dir /oldboyedu/data/redis16371
bind 172.200.1.201 127.0.0.1
requirepass oldboyedu
masterauth oldboyedu
# 配置RDB持久化策略
save 900 1
save 300 10
save 60 10000
# 配置AOF持久化
appendonly yes
appendfsync everysec
[root@redis201.oldboyedu.com ~]#
(3)为"redis16372"实例创建配置文件
[root@redis201.oldboyedu.com ~]# cat > /oldboyedu/softwares/redis16372/redis.conf <<EOF
> port 16372
> daemonize yes
> pidfile /oldboyedu/data/redis16372/redis.pid
> loglevel notice
> logfile /oldboyedu/data/redis16372/redis.log
> dbfilename dump.rdb
> dir /oldboyedu/data/redis16372
> bind 172.200.1.201 127.0.0.1
> requirepass oldboyedu
> masterauth oldboyedu
> # 配置RDB持久化策略
> save 900 1
> save 300 10
> save 60 10000
> # 配置AOF持久化
> appendonly yes
> appendfsync everysec
> EOF
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# cat /oldboyedu/softwares/redis16372/redis.conf
port 16372
daemonize yes
pidfile /oldboyedu/data/redis16372/redis.pid
loglevel notice
logfile /oldboyedu/data/redis16372/redis.log
dbfilename dump.rdb
dir /oldboyedu/data/redis16372
bind 172.200.1.201 127.0.0.1
requirepass oldboyedu
masterauth oldboyedu
# 配置RDB持久化策略
save 900 1
save 300 10
save 60 10000
# 配置AOF持久化
appendonly yes
appendfsync everysec
[root@redis201.oldboyedu.com ~]#
温馨提示:
(1)所有的requirepass建议设置为一致,这为我们"哨兵模式"做准备,很明显,"requirepass"表示配置当前实例的验证口令;
(2)注意"masterauth"表示的含义是主库的验证口令;
下面的模板你可以根据自己业务的实际需求进行改动即可~
cat > /oldboyedu/softwares/redis16372/redis.conf <<EOF
port 16372
daemonize yes
pidfile /oldboyedu/data/redis16372/redis.pid
loglevel notice
logfile /oldboyedu/data/redis16372/redis.log
dbfilename dump.rdb
dir /oldboyedu/data/redis16372
bind 172.200.1.201 127.0.0.1
requirepass oldboyedu
masterauth oldboyedu
# 配置RDB持久化策略
save 900 1
save 300 10
save 60 10000
# 配置AOF持久化
appendonly yes
appendfsync everysec
EOF
3.启动各个Redis实例
(1)启动各个Redis实例
[root@redis201.oldboyedu.com ~]# ss -ntl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:22 *:*
LISTEN 0 128 [::]:22 [::]:*
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# redis-server /oldboyedu/softwares/redis16370/redis.conf
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# redis-server /oldboyedu/softwares/redis16371/redis.conf
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# redis-server /oldboyedu/softwares/redis16372/redis.conf
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# ss -ntl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 127.0.0.1:16370 *:*
LISTEN 0 128 172.200.1.201:16370 *:*
LISTEN 0 128 127.0.0.1:16371 *:*
LISTEN 0 128 172.200.1.201:16371 *:*
LISTEN 0 128 127.0.0.1:16372 *:*
LISTEN 0 128 172.200.1.201:16372 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 128 [::]:22 [::]:*
[root@redis201.oldboyedu.com ~]#
(2)停止各个Redis实例运行,此步骤可跳过,因为我们暂时还不需要停止Redis,我们要用Redis实例做主从架构。
[root@redis201.oldboyedu.com ~]# ss -ntl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 127.0.0.1:16370 *:*
LISTEN 0 128 172.200.1.201:16370 *:*
LISTEN 0 128 127.0.0.1:16371 *:*
LISTEN 0 128 172.200.1.201:16371 *:*
LISTEN 0 128 127.0.0.1:16372 *:*
LISTEN 0 128 172.200.1.201:16372 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 128 [::]:22 [::]:*
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# redis-cli -p 16370 -a oldboyedu shutdown
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# redis-cli -p 16371 -a oldboyedu shutdown
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# redis-cli -p 16372 -a oldboyedu shutdown
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# ss -ntl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:22 *:*
LISTEN 0 128 [::]:22 [::]:*
[root@redis201.oldboyedu.com ~]#
4.开启Redis的主从
实验说明:
以"redis16370"实例为主库,以"redis16371"实例和"redis16372"实例为从库。
从库实例开启主从:
[root@redis201.oldboyedu.com ~]# redis-cli -p 16371 -a oldboyedu SLAVEOF 172.200.1.201 16370
OK
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# redis-cli -p 16372 -a oldboyedu SLAVEOF 172.200.1.201 16370
OK
[root@redis201.oldboyedu.com ~]#
5.主库实例查看主从信息
[root@redis201.oldboyedu.com ~]# redis-cli -p 16370 -a oldboyedu INFO REPLICATION
# Replication
role:master
connected_slaves:2
slave0:ip=172.200.1.201,port=16371,state=online,offset=127,lag=1
slave1:ip=172.200.1.201,port=16372,state=online,offset=127,lag=1
master_repl_offset:127
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:126
[root@redis201.oldboyedu.com ~]#
6.从库实例查看主从信息
(1)在"redis16371"实例中查看主从状态信息:
[root@redis201.oldboyedu.com ~]# redis-cli -p 16371 -a oldboyedu INFO REPLICATION
# Replication
role:slave
master_host:172.200.1.201
master_port:16370
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:379
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
[root@redis201.oldboyedu.com ~]#
(2)在"redis16372"实例中查看主从状态信息:
[root@redis201.oldboyedu.com ~]# redis-cli -p 16372 -a oldboyedu INFO REPLICATION
# Replication
role:slave
master_host:172.200.1.201
master_port:16370
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_repl_offset:379
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
[root@redis201.oldboyedu.com ~]#
7.验证主从配置是否生效
(1)在"redis16370"实例主库的11号数据库创建测试数据
[root@redis201.oldboyedu.com ~]# redis-cli -p 16370 -a oldboyedu -n 11
127.0.0.1:16370[11]> KEYS *
(empty list or set)
127.0.0.1:16370[11]>
127.0.0.1:16370[11]> SET name JasonYin
OK
127.0.0.1:16370[11]>
127.0.0.1:16370[11]> SET age 18
OK
127.0.0.1:16370[11]>
127.0.0.1:16370[11]> KEYS *
1) "age"
2) "name"
127.0.0.1:16370[11]>
127.0.0.1:16370[11]> QUIT
[root@redis201.oldboyedu.com ~]#
(2)在"redis16371"实例从库的11号数据库查看是否同步主库的11号数据库
[root@redis201.oldboyedu.com ~]# redis-cli -p 16371 -a oldboyedu -n 11
127.0.0.1:16371[11]> KEYS *
1) "age"
2) "name"
127.0.0.1:16371[11]>
127.0.0.1:16371[11]> GET name
"JasonYin"
127.0.0.1:16371[11]>
127.0.0.1:16371[11]> GET age
"18"
127.0.0.1:16371[11]>
127.0.0.1:16371[11]> QUIT
[root@redis201.oldboyedu.com ~]#
(2)在"redis16372"实例从库的11号数据库查看是否同步主库的11号数据库
[root@redis201.oldboyedu.com ~]# redis-cli -p 16372 -a oldboyedu -n 11
127.0.0.1:16372[11]> KEYS *
1) "name"
2) "age"
127.0.0.1:16372[11]>
127.0.0.1:16372[11]> GET name
"JasonYin"
127.0.0.1:16372[11]>
127.0.0.1:16372[11]> GET age
"18"
127.0.0.1:16372[11]>
127.0.0.1:16372[11]> QUIT
[root@redis201.oldboyedu.com ~]#
8.解除"redis16372"实例的主从关系
[root@redis201.oldboyedu.com ~]# redis-cli -p 16372 -a oldboyedu info replication
# Replication
role:slave
master_host:172.200.1.201
master_port:16370
master_link_status:up
master_last_io_seconds_ago:8
master_sync_in_progress:0
slave_repl_offset:8240
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# redis-cli -p 16370 -a oldboyedu info replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.200.1.201,port=16371,state=online,offset=8310,lag=1
slave1:ip=172.200.1.201,port=16372,state=online,offset=8310,lag=0
master_repl_offset:8310
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:8309
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# redis-cli -p 16372 -a oldboyedu SLAVEOF NO ONE # 解除"redis16372"实例的主从关系
OK
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# redis-cli -p 16370 -a oldboyedu info replication
# Replication
role:master
connected_slaves:1
slave0:ip=172.200.1.201,port=16371,state=online,offset=8310,lag=0
master_repl_offset:8310
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:8309
[root@redis201.oldboyedu.com ~]#
[root@redis201.oldboyedu.com ~]# redis-cli -p 16372 -a oldboyedu info replication
# Replication
role:master
connected_slaves:0
master_repl_offset:8310
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
[root@redis201.oldboyedu.com ~]#
四.跨节点部署Redis集群实战案例
(0)主从架构设计
主库为:
10.0.0.108
从库:
10.0.0.106
10.0.0.107
(1)10.0.0.106的配置
install -d /oldboyedu/data/redis
cat > /oldboyedu/softwares/redis/conf/redis.conf <<EOF
port 8106
daemonize yes
pidfile /oldboyedu/data/redis/redis.pid
loglevel notice
logfile /oldboyedu/data/redis/redis.log
dbfilename oldboyedu_linux.rdb
dir /oldboyedu/data/redis/
bind 10.0.0.106
requirepass oldboyedu
masterauth oldboyedu
# 配置RDB持久化策略
save 900 1
save 300 10
save 60 10000
EOF
redis-server /oldboyedu/softwares/redis/conf/redis.conf
(2)10.0.0.107的配置
install -d /oldboyedu/data/redis
cat > /oldboyedu/softwares/redis/conf/redis.conf <<EOF
port 8107
daemonize yes
pidfile /oldboyedu/data/redis/redis.pid
loglevel notice
logfile /oldboyedu/data/redis/redis.log
dbfilename oldboyedu_linux.rdb
dir /oldboyedu/data/redis/
bind 10.0.0.107
requirepass oldboyedu
masterauth oldboyedu
# 配置RDB持久化策略
save 900 1
save 300 10
save 60 10000
EOF
redis-server /oldboyedu/softwares/redis/conf/redis.conf
(3)10.0.0.108的配置
install -d /oldboyedu/data/redis
cat > /oldboyedu/softwares/redis/conf/redis.conf <<EOF
port 8108
daemonize yes
pidfile /oldboyedu/data/redis/redis.pid
loglevel notice
logfile /oldboyedu/data/redis/redis.log
dbfilename oldboyedu_linux.rdb
dir /oldboyedu/data/redis/
bind 10.0.0.108
requirepass oldboyedu
masterauth oldboyedu
# 配置RDB持久化策略
save 900 1
save 300 10
save 60 10000
EOF
redis-server /oldboyedu/softwares/redis/conf/redis.conf
(4)从库开启主从
10.0.0.106:
redis-cli -p 8106 -h 10.0.0.106 -a oldboyedu SLAVEOF 10.0.0.108 8108
10.0.0.107:
redis-cli -p 8107 -h 10.0.0.107 -a oldboyedu SLAVEOF 10.0.0.108 8108