开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
主从复制
三个原理介绍
- 当master-replica保持链接时,master会通过发送命令流的方式保证replica始终保持最新;
- 当master-replica断开连接后,replica会主动尝试重新连接master,并执行部分数据同步,把断开期间的数据同步到replica;
- 如果replica执行部分数据同步失败,则会执行全部数据同步,master会把所有数据的快照发送给replica,同时把后续的命令也发送到replica;
Redis使用异步复制机制,replica会异步向master确认周期性接收的数据量,所以master不用每一次都等待复制命令完成,但是通过可选的同步复制,master可以知道replica已经处理了哪些命令。
replica可以使用WAIT告诉master采用同步复制,但是这不能保证数据不丢失,比如服务重启、链接断开之类。
重要特性
- 采用异步复制,replica-to-master采用异步确认已复制的数据量
- 一个master可以拥有多个replicas
- replica除了可以链接master外,也可以链接其他的replica,并且都是接收相同的master复制数据流
- 主从复制对于master是非阻塞的,所以在这期间仍然可以接收和处理请求
- 主从复制对应replica也是非阻塞的,这期间可以使用旧数据对外提供查询请求(可配置),完成复制后,要删除旧数据并加载新数据,4.0之后删除是在新的线程中执行的
- replicas可设置为只读,用于架设具备可伸缩的架构
- 可以让master不做持久化,然后把数据复制到replica后,在replica端执行持久化。但是如果master重启,会导致数据丢失,这个时候如果replica再同步一次,replica也会被清空
不建议关闭master的持久化功能,如果必须关闭,那么必须同时关闭master的自动启动,防止清空所有副本的数据
工作方式
每一个master都有一个复制ID(大的伪随机字符串)标定数据,同时还有一个偏移量,用于标记需要发送给replica的数据增量(及时没有replica链接,该偏移量也会随着数据递增):
Replication ID, offset
只有replication id相同的master和replica才认为有相同的数据。
redis实例有两个replication id(主id和辅助id),当replica提升为master后,需要首先用原来的主id和其他replicas进行同步,稍后再把辅助ID切换成主ID,这样之前的主ID就失效了,如果以后死掉的master再启动,也不会因为原来的ID造成混乱
replica链接master时,使用PSYNC命令发送上次的 Replication ID和已处理的offset,这样master就知道下次再发送哪些数据了。如果master中没有足够的backlog,master会发送完整的数据副本给该replica,replica会从头获取全部的数据。
复制流程
- master启动一个后台保存进程,以便生成RDB文件(无论多少replicas,都只有一个保存进程)
- 同时缓冲从客户端接收到的所有新的写命令
- 主机将数据库文件传输到副本
- 副本将其保存在磁盘上,然后将其加载到内存中
- 主服务器将所有缓冲后的命令发送到副本(这是一个命令流)
为了解决硬盘慢的问题,2.8版本开始支持在内存中直接把rdb发送给replica,不用保存到硬盘了,配置项:
repl-diskless-sync
配置
# 在replica配置中添加以下配置,设置为从库
replicaof 192.168.1.1 6379
# 也可以通过执行以下命令,同步一次数据
replicaof host port
只读副本
自2.6版本后,可以使用replica-read-only命令配置是否为只读副本(默认是只读副本)。
只读副本同样允许使用CONFIG SET执行命令,包括关闭只读副本,所以也不安全。
redis4.0开始,副本的可写是在本地进行的,所以不会传播到子副本,如下:
master ---> replica A ---> replica B
如果 replica A可写,新增的内容不会被复制到 replica B,所以 replica B始终和master一致
安全
master 在配置文件中,可以通过 requirepass 123456 设置密码;
replica 在配置文件中,可以通过 masterauth 123456 设置对应master的密码;
当然,也可以使用 config set config_key config_value的方式进行临时配置
持久化
支持两种类型持久化:
RDB(redis database):按指定的时间间隔执行数据集的快照;AOF(append only file):记录服务器接收的每一个写命令,然后在服务器重启时,重新执行这些命令,用以重建完整的数据库;当记录的日志文件太大后,redis可以在后台对日志进行重写,以删除没用的命令;
一般RDB和AOF配合使用,同时存在时,服务重启时首先使用AOF重建数据库
RDB持久化
RDB文件是一个压缩后的二进制文件,通过该文件可以还原数据库状态。
有三个命令可以生成RDB文件:save、bgsave和flushall,这三个命令都会调用 rdb.c/rdbSave函数生成RDB文件。
save
该命令同步指定RDB文件的生成,生成RDB文件期间,不再处理客户端发来的请求
bgsave
该命令会派生子进程生成RDB文件,主进程继续处理请求。
虽然是在子进程中进行的,但是在执行bgsave期间,客户端再次发送save或bgsave命令,redis会拒绝执行,是为了防止冲突。
自动保存
自动保存实际调用的也是bgsave,通过在配置文件中添加配置,可执行定时bgsave操作,允许同时配置多个触发条件,只要有一个满足即触发保存:
save 900 1 # 900s 内对数据库进行了1次修改
save 300 10 # 300s 内对数据库进行了10次修改
save 60 10000 # 60s 内对数据进行了 10000次修改
flushall
会清空redis中所有的数据库,记住是所有数据库下的所有数据,这个时候,只要自动保存条件不为空,就会执行一次保存(即使不满足自动保存条件也会触发)。
原理
在 redis-server的内存中有一个数组saveparams记录着上述配置,如下:
另外,redis-server内存中还记录着两个变量dirty计数器和lastsave时间戳:
- dirty计数器:自上次执行save\bgsave之后进行了多少次修改
- lastsave时间戳:上次成功执行save\bgsave的时间
执行批量插入时,dirty会累加多次,如sadd database redis mysql mongodb,dirty会增加3次。
检查执行
在redis-server中有一个周期性函数serverCron,每隔100毫秒检查一次,如果满足条件就执行一次 bgsave。
通过修改一下配置指定dump.rdb文件位置,执行后查看是否生成备份文件。
dir ./
dbfilename dump.rdb
还原数据库
Redis在重新启动时,会通过调用rdb.c/rdbLoad函数,自动加载RDB文件,并还原关闭前的数据,所以没有提供手动还原的命令。
Redis在还原数据期间,服务处于阻塞状态,不处理客户端的请求。
因为AOF文件更新频率更高,数据更新,所以如果Redis在启动时发现开启了AOF功能,将首先加载AOF文件来还原数据库;只有AOF关闭时才会加载RDB。
RDB文件结构
整体构成
database构成
如果0和3号数据库有数据,整体构成文件如下:
| REDIS | db_version | database0 | database3 | EOF | check_sum
通过od命令可以查看dump.rdb文件
[wdy@data]# od -c dump.rdb
0000000 R E D I S 0 0 0 9 372 \t r e d i s
0000020 - v e r 005 6 . 2 . 6 372 \n r e d i
0000040 s - b i t s 300 @ 372 005 c t i m e 302
0000060 017 261 326 a 372 \b u s e d - m e m 302 \b
0000100 362 \r \0 372 \f a o f - p r e a m b l
0000120 e 300 \0 376 \0 373 001 \0 \0 004 n a m e 300 {
0000140 377 214 234 274 H " 373 ' 375
0000151
AOF持久化
RDB会保存数据库中的数据,AOF会记录redis服务器执行的写命令,正好互补吧。
默认AOF功能是禁用的,可以通过修改配置文件开启:
appendonly yes
# AOF保存的路径和RDB相同,都是通过 dir 配置
appendfilename appendonly.aof
每执行一次修改redis数据库的操作,redis就会把该命令写入AOF文件。
实现原理
AOF分命令追加、文件写入和文件同步三个步骤。
- 命令追加 每次执行修改命令后,redis会把命令追加到aof_buf缓冲区,并不是立即写入硬盘。
- 文件写入 和RDB一样,也是由 serverCron函数执行检测,通过调用flushAppendOnlyFile函数把aof_buf数据写入到AOF文件。
影响flushAppendOnlyFile执行的配置是appendfsync:
把aof_buf内容写入AOF文件后,实际并没有写入硬盘,而是由操作系统缓存,默认会30s执行一次同步硬盘操作,这期间如果宕机,数据就会丢失。
为了解决30s的损失,redis提供了everysec配置
还原数据库
Redis在重启时,会启动一个伪客户端(无网络连接功能),用于加载AOF文件,并重新执行所有命令,把数据写入数据库。
AOF重写
AOF文件虽然像写日志一样记录所有命令,但是也存在一个问题,比如多次执行相同命令,就会存在很多重复的内容,而只有最后一条命令才有效,这时候就会需要AOF重写。
重写时,redis并不会读取AOF文件并分析+重写,实际上redis是开启了子线程来把数据库中的所有数据写入一个新的AOF文件,从而保证新AOF文件中没有冗余数据。
影响重写的配置文件:
# 当目前AOF文件大小超过上一次重写时AOF大小的百分比,超过了就会再次重写
auto-aog-rewrite-percentage 100
# 限制重写的最小AOF大小,这样当AOF很小时,就不用重写了
auto-aof-rewrite-min-size 64mb
提示: 实际上我们还可以手动调用 bgrewriteaof命令,手动触发AOF重写