Redis主从复制

主从复制

就是主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主。

用处

读写分离,性能扩展

容灾快速恢复

示意图:

 

配置Redis

1、拷贝多个redis.conf文件include,因为redis.conf可以配置共有的配置。如果有不同,include后,可以自行配置,会覆盖redis.conf中的配置。

 

 

 首先在主目录下,新建master-slave文件夹(其实就是随便建个目录放文件),里面添加不同端口的配置文件,起3个服务,形成三个redis节点。

 同时include共有的配置:

 

然后,共有的配置到redis.conf中修改,不同的配置在单独的配置文件中修改。

2、开启daemonize yes

使用的是一台机器启动多个服务的方法,所以需要配置后台启动。

在redis.conf中:

# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize yes

3、Pid文件名字pidfile

这个在redis.conf中找到,各自配置下,如果是单独的机器,这个不用配置:

pidfile /var/run/redis_6379.pid

 

 

4、指定端口

port 6379

 

5、Log文件名字

logfile /usr/local/src/bchen/redis-5.0.8/master-slave/file_6379.log

 

6、Dump.rdb名字dbfilename,涉及到一个文件名称和一个路径名称

dbfilename dump_6379.rdb
dir /usr/local/src/bchen/redis-5.0.8/master-slave

 

7、Appendonly 关掉或者换名字

这个是aof持久化相关的,我们可以不用配置,直接关了就好,有开rdb就可以了。直接在共有的redis.conf中配置。

appendonly no

其实默认就是no。

 

8、其他实例也配置下,最终配置文件长这样:

6379:

include /usr/local/src/bchen/redis-5.0.8/redis.conf
pidfile /var/run/redis_6379.pid
port 6379
logfile /usr/local/src/bchen/redis-5.0.8/master-slave/file_6379.log
dbfilename dump_6379.rdb
dir /usr/local/src/bchen/redis-5.0.8/master-slave

6380:

include /usr/local/src/bchen/redis-5.0.8/redis.conf
pidfile /var/run/redis_6379.pid
port 6379
logfile /usr/local/src/bchen/redis-5.0.8/master-slave/file_6379.log
dbfilename dump_6380.rdb
dir /usr/local/src/bchen/redis-5.0.8/master-slave

6381:

include /usr/local/src/bchen/redis-5.0.8/redis.conf
pidfile /var/run/redis_6379.pid
port 6379
logfile /usr/local/src/bchen/redis-5.0.8/master-slave/file_6379.log
dbfilename dump_6381.rdb
dir /usr/local/src/bchen/redis-5.0.8/master-slave

 

启动

启动命令

查看进程:

 

 三个pid文件:

 

 日志文件:

 

 

搭建简单的一主二仆模式

这三个实例其实现在是互不关联的,需要额外的操作使之互相起作用。

 先熟悉两个重要的命令:

info replication:打印主从复制的相关信息

slaveof  <ip>  <port> : 成为某个实例的从服务器,相当于现在的小弟找大哥。

 

先三台机器各自执行下,info replication命令:

 

 

 

 可以看到初始角色全部都是master。

 接下来把6380和6381配置slave。

192.168.71.131:6381> slaveof 192.168.71.131 6379
OK
192.168.71.131:6380> slaveof 192.168.71.131 6379
OK

 

查看下主从关系:

192.168.71.131:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.71.131,port=6381,state=online,offset=392,lag=0
slave1:ip=192.168.71.131,port=6380,state=online,offset=392,lag=0
master_replid:119b5221a7a59fa0bae8b405aa9bc09074ee0c11
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
192.168.71.131:6380> info replication
# Replication
role:slave
master_host:192.168.71.131
master_port:6379
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_repl_offset:378
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:119b5221a7a59fa0bae8b405aa9bc09074ee0c11
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:378
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:323
repl_backlog_histlen:56
192.168.71.131:6381> info replication
# Replication
role:slave
master_host:192.168.71.131
master_port:6379
master_link_status:up
master_last_io_seconds_ago:9
master_sync_in_progress:0
slave_repl_offset:392
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:119b5221a7a59fa0bae8b405aa9bc09074ee0c11
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

可以看到6379已经变成了master, 6380和6381为slave。

 

测试主从复制

 

尝试从slave(6380)写入:

192.168.71.131:6380> select 0
OK
192.168.71.131:6380> keys *
(empty list or set)
192.168.71.131:6380> set k1 v1
(error) READONLY You can't write against a read only replica.

报错!

尝试从master写入:

192.168.71.131:6379> 
192.168.71.131:6379> SELECT 0
OK
192.168.71.131:6379> keys *
(empty list or set)
192.168.71.131:6379> set k1 v1
OK
192.168.71.131:6379> get k1
"v1"

可以看到master可以写入也可以读取。

再从slave中读取数据:

192.168.71.131:6380> get  k1
"v1"

6380中可以读取到,同样的6381中也可以读取到。

 

问题刨析

1、从机是否可以写?set可否?

以上已经测试过了,可以看出从机不能写,只能读。

 

2、切入点问题。slave第一次加入进来,是从头开始复制还是从切入点开始复制。

先把6380(或者6381)关闭掉,然后启动,删除所有数据,然后重新接入到6379成为slave,看下6380能否有之前的数据。

 

 可以看到没有6380这个进程,然后看下6379的主从关系。

[root@localhost src]# ./redis-cli -h 192.168.71.131 -p 6379
192.168.71.131:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.71.131,port=6381,state=online,offset=12652,lag=1
master_replid:119b5221a7a59fa0bae8b405aa9bc09074ee0c11
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:12652
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:12652

这时候没有了6380这个slave。

 

启动6380

[root@localhost src]# ./redis-server ../master-slave/redis6380.conf 
[root@localhost src]# ps -ef | grep redis
root      86740      1  0 03:26 ?        00:00:12 ./redis-server 192.168.71.131:6379
root      86750      1  0 03:26 ?        00:00:11 ./redis-server 192.168.71.131:6381
root      86941  86919  0 07:33 pts/0    00:00:00 ./redis-cli -h 192.168.71.131 -p 6379
root      86967      1  0 07:36 ?        00:00:00 ./redis-server 192.168.71.131:6380
root      86972  86946  0 07:36 pts/2    00:00:00 grep --color=auto redis

 

再次查看6379:

192.168.71.131:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.71.131,port=6381,state=online,offset=13002,lag=1
master_replid:119b5221a7a59fa0bae8b405aa9bc09074ee0c11
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:13002
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:13002

可以看出,slave不会保存上次的记忆,不会自动成为79的从机。

这时候,我们把6380的数据全部清空。

[root@localhost src]# ./redis-cli -h 192.168.71.131 -p 6380
192.168.71.131:6380> keys *
1) "k1"
192.168.71.131:6380> FLUSHALL
OK
192.168.71.131:6380> keys *
(empty list or set)

然后再让它成为6379的从机:

192.168.71.131:6380> slaveof 192.168.71.131 6379
OK
192.168.71.131:6380> keys *
1) "k1"

发现6380已经把全部数据同步过来了。并从新成为6379的slave。

当一个全新的节点成为某个master的slave的时候,他会从master那边同步一份最全的数据过来,而并不是从切入点开始复制。

 

3、其中一台从机运行了一段时间,然后down后情况如何?再次接入后,是怎么样的?

 shutdown 8081

192.168.71.131:6381> shutdown
not connected> 

查看6379(要稍微等一会)

192.168.71.131:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.71.131,port=6380,state=online,offset=25070,lag=1
master_replid:119b5221a7a59fa0bae8b405aa9bc09074ee0c11
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:25070
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:25070
192.168.71.131:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.71.131,port=6380,state=online,offset=25140,lag=0
master_replid:119b5221a7a59fa0bae8b405aa9bc09074ee0c11
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:25140
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:25140

然后对6379写入新的键值对:

192.168.71.131:6379> set k2 v2
OK
192.168.71.131:6379> set k3 v3
OK
192.168.71.131:6379> keys *
1) "k3"
2) "k1"
3) "k2"
192.168.71.131:6380> keys *
1) "k3"
2) "k2"
3) "k1"

在6379和6380中都可以看到值的变化。

 

这时候启动8081,并执行slaveof命令,成为6379的slave。

[root@localhost src]# ./redis-server ../master-slave/redis6381.conf 
[root@localhost src]# ps -aux | grep redis
root      86740  0.0  0.0 145188  2300 ?        Ssl  03:26   0:20 ./redis-server 192.168.71.131:6379
root      86967  0.0  0.0 145188  2316 ?        Ssl  07:36   0:07 ./redis-server 192.168.71.131:6380
root      87119  0.0  0.0  14080  1208 pts/0    S+   09:56   0:00 ./redis-cli -h 192.168.71.131 -p 6379
root      87120  0.0  0.0  14080  1200 pts/1    S+   09:56   0:00 ./redis-cli -h 192.168.71.131 -p 6380
root      87145  0.0  0.0 144024  2040 ?        Ssl  10:06   0:00 ./redis-server 192.168.71.131:6381
root      87150  0.0  0.0 112720   952 pts/2    S+   10:06   0:00 grep --color=auto redis
[root@localhost src]# ./redis-cli -h 192.168.71.131 -p 6381
192.168.71.131:6381> keys *
1) "k1"
192.168.71.131:6381> SLAVEOF 192.168.71.131 6379
OK
192.168.71.131:6381> keys *
1) "k2"
2) "k3"
3) "k1"

可以看到重新成为slave的时候,可以重新拿数据,从而跟上大部队。

 

4、主机shutdown后情况如何?从机是上位成为master还是原地待命

192.168.71.131:6379> SHUTDOWN
not connected> exit

关闭6379后。

稍微等会,再查看6380和6381的情况

192.168.71.131:6380> info replication
# Replication
role:slave
master_host:192.168.71.131
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:25949
master_link_down_since_seconds:113
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:119b5221a7a59fa0bae8b405aa9bc09074ee0c11
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:25949
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:13311
repl_backlog_histlen:12639
192.168.71.131:6381> info replication
# Replication
role:slave
master_host:192.168.71.131
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:25949
master_link_down_since_seconds:148
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:119b5221a7a59fa0bae8b405aa9bc09074ee0c11
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:25949
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:25698
repl_backlog_histlen:252

可以看到如果slave没有人为的额外的操作,仍然只是一个slave的角色。 不会上位成为master。

 

5、主机又回来了后,主机新增记录,从机还能否顺利复制?

现在我们启动6379

[root@localhost src]# ./redis-server ../master-slave/redis6379.conf
[root@localhost src]# ps -aux | grep redis
root      86967  0.0  0.0 145184  2308 ?        Ssl  07:36   0:07 ./redis-server 192.168.71.131:6380
root      87120  0.0  0.0  14080  1204 pts/1    S+   09:56   0:00 ./redis-cli -h 192.168.71.131 -p 6380
root      87145  0.0  0.0 145168  2280 ?        Ssl  10:06   0:00 ./redis-server 192.168.71.131:6381
root      87151  0.0  0.0  14080  1212 pts/2    S+   10:07   0:00 ./redis-cli -h 192.168.71.131 -p 6381
root      87155  0.0  0.0 145172  2232 ?        Ssl  10:17   0:00 ./redis-server 192.168.71.131:6379
root      87162  0.0  0.0 112720   956 pts/0    S+   10:17   0:00 grep --color=auto redis

查看下信息:

192.168.71.131:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.71.131,port=6380,state=online,offset=210,lag=0
slave1:ip=192.168.71.131,port=6381,state=online,offset=196,lag=1
master_replid:33bfcec0a2af6009b3e21df66a6bac7aeabcf64d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:210
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:210

测试能否正常工作:

192.168.71.131:6379> set k4 v4
OK
192.168.71.131:6380> get k4
"v4"
192.168.71.131:6381> get k4
"v4"

可以正常工作。

当down掉的master重新回来的时候,是不用额外的操作,redis自动会维持原有的主从关系,并可以正常工作。

 

复制原理

1、每次从机联通后,都会给主机发送sync指令

2、主机立刻进行存盘操作,发送RDB文件,给从机

3、从机收到RDB文件后,进行全盘加载

4、之后每次主机的写操作,都会立刻发送给从机,从机执行相同的命令

 

 

搭建树形的主从关系

 上面的关系,主要存在的问题是: master如果下面有多个slave那么master的同步操作开销很大,因为从机数量太多。

我们由此想到:上一个slave可以是下一个slave的Master,slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了链条中下一个的master, 可以有效减轻master的写压力,去中心化降低风险。

 缺点: 风险是一旦某个slave宕机,后面的slave都没法备份。因为这是一种类似于树形的主从关系。如果有一个slave节点的挂了,那么对应的所有子树将全部没办法写了。

我们可以测试搭建6379->6380->6381的这种主从关系。

[root@VM_0_8_centos src]# ./redis-cli -h localhost -p 6380
localhost:6380> slaveof localhost 6379
OK
localhost:6380> 
127.0.0.1:6381> slaveof 127.0.0.1 6380
OK

我们查看6380,可以看出这个时候,角色是slave, 但是有对于6379来讲是从机,对于6381来讲是主机:

 

虽然对于6381来讲是主机,但是也是不允许写入的。我们可以测试如下:

127.0.0.1:6380> set k2 v2
(error) READONLY You can't write against a read only replica.

 

哨兵机制

我门想到一个问题:如果以上的主从关系中:6379->6380->6381,当6379挂掉了以后:如何让6380上位?

即执行:用 slaveof  no one  将从机变为主机。(必须手动执行)。

哨兵机制其实就是反客为主的自动版。

 

我首先建个sentinel.conf的文件,然后填写:

sentinel  monitor  mymaster  127.0.0.1  6379  1

其中mymaster为监控对象起的服务器名称, 1 为 至少有多少个哨兵同意迁移的数量。

启动哨兵, 通过src目录下的redis-sentinel:

 

 

 

 

 

 这边哨兵也可以自动获取到主从关系。

 

现在手动shutdown 6379看下,会怎么样:

 

 

从哨兵的日志可以看到,其实现在已经是6380做master了。可以验证下:

127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6381,state=online,offset=15719,lag=0
master_replid:9fdc923e373621d1830cf52024edd2f1fc32096b
master_replid2:5cdcc9b49def111a61014d745996f93bc9be07a4
master_repl_offset:15719
second_repl_offset:5099
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:15719
127.0.0.1:6380> 

那如果现在重新启动6379,情况会怎么样?其实从哨兵的日志可以看出来,6379已经编程6380的slave了。我们再启动6379,然后验证:

 

总结:

当主机挂了后,哨兵会选择一个从机作为slave, 选择条件依次为: 1、选择优先级靠前的 2、选择偏移量最大的 3、选择runid最小的从服务

详解:什么是优先级靠前的?这个就要看主配置文件:slave-priority, (注:本人测试版本5.0.8,这个版本配置已经改成replica-priority),数字越小表示优先级越大,0表示永远不会被选为master。
那什么是偏移量最大的?因为master需要向slave写数据,当master 挂了以后,那个时刻可能各个slave同步到的数据有细微差异,会优先选择同步数据更多的。

 那runid指每个redis实例启动后都会随机生成一个40位的runid。

posted @ 2020-05-06 22:00  护花使者  Views(367)  Comments(0Edit  收藏  举报