redis主从同步/复制

Redis主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布 记录。同步对读取操作的可扩展性和数据冗余很有帮助。

工作原理

Redis的主从结构可以采用一主多从或者级联结构,Redis主从复制可以根据是否是全量分为全量同步和增量同步。

全量同步

  Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:

  1)从服务器连接主服务器,发送SYNC命令;

  2)主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;

  3)主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;

  4)从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;

  5)主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;

  6)从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;

完成上面几个步骤后就完成了从服务器数据初始化的所有操作,从服务器此时可以接收来自用户的读请求。

增量同步

  Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。

增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。

Redis主从同步策略

  主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。

注意点

如果多个Slave断线了,需要重启的时候,因为只要Slave启动,就会发送sync请求和主机全量同步,当多个同时出现的时候,可能会导致Master IO剧增宕机。

示例

环境准备

1
2
3
server1:0.0.0.0:6379	redis-master
server2:0.0.0.0:6380 redis-slave
server3:0.0.0.0:6381 redis-slave

6379配置文件

1
2
3
4
5
6
7
8
root@Tony-PC:/tmp/redis-config/6379# cat 6379.conf
daemonize yes
port 6379
pidfile /tmp/redis-config/6379/6379.pid
loglevel notice
logfile /tmp/redis-config/6379/6379.log
dir /tmp/redis-config
dbfilename 6379.rdb

6380配置文件:

使用sed命令,将6379.conf中的的6379替换为6380,替换后的结果写入到6380.conf

1
root@Tony-PC:/tmp/redis-config/6379# sed "s/6379/6380/g" redis-6379.conf > ../6380/6380.conf
1
2
3
4
5
6
7
8
9
root@Tony-PC:/tmp/redis-config/6379# cat ../6380/6380.conf
daemonize yes
port 6380
pidfile /tmp/redis-config/6380/6380.pid
loglevel notice
logfile /tmp/redis-config/6380/6380.log
dir /tmp/redis-config
dbfilename 6380.rdb
slaveof 127.0.0.1 6379 # 指明主的地址

6381配置文件:

使用sed命令,将6379.conf中的的6379替换为6381,替换后的结果写入到6381.conf

1
root@Tony-PC:/tmp/redis-config/6379# sed "s/6379/6381/g" redis-6379.conf > ../6381/6381.conf
1
2
3
4
5
6
7
8
9
root@Tony-PC:/tmp/redis-config/6379# cat ../6381/6381.conf
daemonize yes
port 6381
pidfile /tmp/redis-config/6381/6381.pid
loglevel notice
logfile /tmp/redis-config/6381/6381.log
dir /tmp/redis-config
dbfilename 6381.rdb
slaveof 127.0.0.1 6379 # 指明主的地址

启动redis3个服务

1
2
3
4
5
6
7
8
9
10
11
12
13
root@Tony-PC:/tmp/redis-config/6379# redis-server 6379.conf
root@Tony-PC:/tmp/redis-config/6379# redis-server ../6380/6380.conf
root@Tony-PC:/tmp/redis-config/6379# redis-server ../6381/6381.conf

root@Tony-PC:/tmp/redis-config/6379# ps -ef | grep redis
root 5570 5546 0 10:54 pts/3 00:00:00 vim /etc/redis/redis.conf
tony 5782 1 0 10:55 ? 00:00:00 /bin/bash /usr/bin/dde-file-manager-pkexec file:///var/log/redis
root 5784 5782 0 10:55 ? 00:00:43 /usr/bin/dde-file-manager file:///var/log/redis -w /home/tony
root 14081 7206 0 14:45 pts/4 00:00:00 redis-cli
root 16443 1 0 15:48 ? 00:00:00 redis-server *:6379
root 16448 1 0 15:48 ? 00:00:00 redis-server *:6380
root 16454 1 0 15:49 ? 00:00:00 redis-server *:6381
root 16464 7362 0 15:49 pts/5 00:00:00 grep redis

查看主从状态

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
root@Tony-PC:~# redis-cli
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=252,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=252,lag=1
master_replid:b8566352985ea4d4dd23a1d2a6f333f02c1d5c0c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:252
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:252


root@Tony-PC:/tmp/redis-config# redis-cli -p 6380
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_repl_offset:266
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:b8566352985ea4d4dd23a1d2a6f333f02c1d5c0c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:266
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:266


root@Tony-PC:/tmp/redis-config# redis-cli -p 6381
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:280
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:b8566352985ea4d4dd23a1d2a6f333f02c1d5c0c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:280
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:15
repl_backlog_histlen:266

测试同步

执行代码发现redis-slave能同步读取redis-master上的信息,且只有redis-master允许写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
127.0.0.1:6379> set country china
OK
127.0.0.1:6379> keys *
1) "country"
127.0.0.1:6379> get country
"china"


127.0.0.1:6380> set name tony
(error) READONLY You can't write against a read only slave.
127.0.0.1:6380> keys *
1) "country"
127.0.0.1:6380> get country
"china"


127.0.0.1:6381> get country
"china"
127.0.0.1:6381> set name aaa
(error) READONLY You can't write against a read only slave.
127.0.0.1:6381> keys *
1) "country"

主库故障切换主从库

关闭主库

1
2
127.0.0.1:6379> shutdown
not connected>

查看从库状态

检查从库主从信息,此时master_link_status:down

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
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1782
master_link_down_since_seconds:13
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:b8566352985ea4d4dd23a1d2a6f333f02c1d5c0c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1782
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1782


127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1782
master_link_down_since_seconds:20
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:b8566352985ea4d4dd23a1d2a6f333f02c1d5c0c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1782
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:15
repl_backlog_histlen:1768

重新设置主从库

将6380设为主库,6381设为6380的从库

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6380> SLAVEOF no one
OK

----------------------------------

127.0.0.1:6381> SLAVEOF no one
OK
127.0.0.1:6381> SLAVEOF 127.0.0.1 6380
OK

查看更新后的主从库状态

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
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6381,state=online,offset=2034,lag=0
master_replid:7851d7e67129a0483b2925d7ac06dda221123cbe
master_replid2:b8566352985ea4d4dd23a1d2a6f333f02c1d5c0c
master_repl_offset:2034
second_repl_offset:1783
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:2034


127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6380
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:1852
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:7851d7e67129a0483b2925d7ac06dda221123cbe
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1852
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1783
repl_backlog_histlen:70