目录
1.配置主从节点
主从关系的配置有三种方式:
- 配置式:根据缓存学习(五):Redis安装、配置,在redis.conf配置slaveof参数
- 启动参数式:在启动redis-server时,使用--slaveof选项配置
- 命令式:使用slaveof命令
需要注意的是,如果主节点配置了requirepass,从节点需要配置materauth才能连接,下面是一个连接示例(6379端口实例为主节点,此处启动的6380端口实例为从节点):
root@Yhc-Surface:~# redis-server --port 6380 --slaveof localhost 6379
...
24:S 28 Apr 2019 14:31:19.041 * Connecting to MASTER localhost:6379
24:S 28 Apr 2019 14:31:19.046 * MASTER <-> REPLICA sync started
24:S 28 Apr 2019 14:31:19.048 * Non blocking connect for SYNC fired the event.
24:S 28 Apr 2019 14:31:19.049 * Master replied to PING, replication can continue...
24:S 28 Apr 2019 14:31:19.050 * Partial resynchronization not possible (no cached master)
24:S 28 Apr 2019 14:31:19.056 * Full resync from master: 1197e315344ccb5e97581f126ca28388080feba1:0
24:S 28 Apr 2019 14:31:19.082 * MASTER <-> REPLICA sync: receiving 1070 bytes from master
24:S 28 Apr 2019 14:31:19.084 * MASTER <-> REPLICA sync: Flushing old data
24:S 28 Apr 2019 14:31:19.084 * MASTER <-> REPLICA sync: Loading DB in memory
24:S 28 Apr 2019 14:31:19.086 * MASTER <-> REPLICA sync: Finished with success
可以看到,6380节点启动后自动连接主节点并进行了一次全量复制。 此时在6379插入一条数据,在6380进行查询:
127.0.0.1:6379> set hello world
OK
127.0.0.1:6380> get hello
"world"
断开连接只需要在从节点执行slaveof no one命令即可,同理,如果要更换主节点,也只需要使用slaveof命令即可,但是更换主节点后,原本的数据会丢失(断开与主节点的连接则不会清空数据):
127.0.0.1:6380> slaveof localhost 6381
OK
127.0.0.1:6380> get hello
(nil)
在Redis副本机制中,会在初次连接以及长时间断开连接时进行全量复制,之后每次执行写入操作都会自动复制,如果短期断开连接,则从会在重新连接后,进行增量同步,无论哪种复制方式,都默认是异步的。在默认情况下,从节点只处理读请求。
Redis副本有三种结构:
最简单的是一主一从结构,这种方式可以帮助主节点分担至少一半的读请求,此外还可以在主节点处关闭持久化,将持久化的压力转移到从节点上,不过这样做的一个后果是,如果主节点下线后又立即重启,会因为没有日志或快照,导致主节点数据清空,经过同步后把从节点的数据也清空了。这种时候,需要先断开主从节点之间的连接,然后重启主节点,再倒转主从关系。
第二种结构是星型结构,即一主多从,这种结构适合读多写少的场景,缺点是初次连接时容易造成主节点拥塞,一个改进思路是让从节点分批上线。
第三种结构是树状结构,即某些从节点作为其他从节点的主节点,实现级联复制,这样一来可以避免多写对主节点造成的压力,二来可以实现指数级的数据扩散速度,缺点就是比较难管理。
Redis副本的相关信息可以通过info replication或role命令查看,示例如下:
127.0.0.1:6379> role
1) "master"
2) (integer) 8637
3) 1) 1) "127.0.0.1"
2) "6380"
3) "8637"
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=8637,lag=1
master_replid:32e1f0aa4381c3fd906a21b7a748ff1fa93be2b2
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:8637
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:8637
对于role命令,其在主节点上输出有三部分,1)是其角色,有master和slave、sentinel三种,2)是主节点复制偏移量,3)是连接的从节点。在从节点上输出有5部分,分别是:角色(slave)、主节点IP、主节点端口、复制状态(connected、connecting、sync三种)、一共从主节点接收了多少数据。sentinel的下面再介绍。
对于info replication,内容比较长,但是自描述性较好就不做介绍了。
2.复制原理
2.1 复制流程
2.1.1 全量复制
首先看一下主从双方的日志,首先是主节点;
192:M 28 Apr 2019 15:25:47.935 * Replica 127.0.0.1:6380 asks for synchronization
192:M 28 Apr 2019 15:25:47.936 * Full resync requested by replica 127.0.0.1:6380
192:M 28 Apr 2019 15:25:47.936 * Starting BGSAVE for SYNC with target: disk
192:M 28 Apr 2019 15:25:47.948 * Background saving started by pid 200
200:C 28 Apr 2019 15:25:47.959 * DB saved on disk
192:M 28 Apr 2019 15:25:48.050 * Background saving terminated with success
192:M 28 Apr 2019 15:25:48.052 * Synchronization with replica 127.0.0.1:6380 succeeded
然后是从节点:
196:S 28 Apr 2019 15:25:47.922 * Connecting to MASTER localhost:6379
196:S 28 Apr 2019 15:25:47.931 * MASTER <-> REPLICA sync started
196:S 28 Apr 2019 15:25:47.931 * Non blocking connect for SYNC fired the event.
196:S 28 Apr 2019 15:25:47.933 * Master replied to PING, replication can continue...
196:S 28 Apr 2019 15:25:47.933 * Partial resynchronization not possible (no cached master)
196:S 28 Apr 2019 15:25:47.950 * Full resync from master: 32e1f0aa4381c3fd906a21b7a748ff1fa93be2b2:0
196:S 28 Apr 2019 15:25:48.051 * MASTER <-> REPLICA sync: receiving 175 bytes from master
196:S 28 Apr 2019 15:25:48.053 * MASTER <-> REPLICA sync: Flushing old data
196:S 28 Apr 2019 15:25:48.056 * MASTER <-> REPLICA sync: Loading DB in memory
196:S 28 Apr 2019 15:25:48.057 * MASTER <-> REPLICA sync: Finished with success
根据以上内容,可以推断出如下复制流程:
- 首先,从节点连接主节点,由于是初次连接,因此,连接成功后需要进行全量复制,为了确保可以进行复制,首先进行了一次PING
- 如果PING成功,则由从节点发出SYNC信号,发起全量同步事件
- 主节点收到SYNC后,开始BGSAVE操作,将当前数据保存为快照
- 快照保存完毕后,开始发送给从节点,从节点清空旧数据并载入主节点发来的新数据
SYNC是Redis的同步协议,不过目前已经改用PSYNC,后者支持部分同步,如果直接向6379端口发送SYNC,会有如下输出:
root@Yhc-Surface:~# echo 'SYNC' | nc localhost 6379
$410
REDIS0009 redis-ver5.0.4
redis-bits@ctime¨]used-memB repl-stream-db repl-id(32e1f0aa4381c3fd906a21b7a748ff1fa93be2b2repl-offsetMaof-pre:hobbymalet@K@__ K programming
sexualage redisgood helloworld testmyteststream jb << nameagesexual zhangsan male jb dO*1
$4
PING
*1
$4
PING
*1
$4
PING
... //每隔几秒输出一次 *1\r\n$4\r\nPING\r\n
SYNC实际就是传输RDB的内容。PSYNC会在下面介绍。
可以看到,当进行全量同步时,有如下日志:
Full resync from master: 32e1f0aa4381c3fd906a21b7a748ff1fa93be2b2:0
这里的master被一个字符串标记了,以冒号为分隔,由两部分组成,前面较长的一串被称为副本ID,是一个伪随机串,用来标记数据集,后面的0是偏移量,用来标记从数据集的哪一部分开始复制,这里由于是全量复制,所以肯定是从0开始。
该流程建立在没有开启 repl-diskless-sync 的情况下,该选项开启后不会先保存RDB再发送,而是直接发送数据
以上两段日志未能体现的是:1)如果在复制期间有写请求到达主节点,这些请求正常响应后,都会存入backlog队列,等到快照发送完毕后再发给从节点 2)从节点如果开启了AOF,则会再同步完毕后立刻执行一次rewrite
2.1.2 断点续传
上面的复制流程是全量复制的,当主从节点断开一段时间后(从节点关闭,不能使用slave no one,后者会导致从节点进入主节点模式,导致副本ID变化,使得每次重连都会进行全量同步)