RDB算法
RDB是将当前数据生成快照保存到硬盘上。数据库
RDB的工做流程:express
1. 执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进程,如RDB/AOF子进程,若是存在bgsave命令直接返回。promise
2. 父进程执行fork操做建立子进程,fork操做过程当中父进程被阻塞。网络
3. 父进程fork完成后,bgsave命令返回“* Background saving started by pid xxx”信息,并再也不阻塞父进程,能够继续响应其余命令。app
4. 父进程建立RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。根据lastsave命令能够获取最近一次生成RDB的时间,对应info Persistence中的rdb_last_save_time。运维
5. 进程发送信号给父进程表示完胜,父进程更新统计信息。性能
对于大多数操做系统来讲,fork都是个重量级操做,虽然建立的子进程不须要拷贝父进程的物理内存空间,可是会复制父进程的空间内存页表。spa
子进程经过fork操做产生,占用内存大小等同于父进程,理论上须要两倍的内存来完成持久化操做,但Linux有写时复制机制(copy-on-write)。父子进程会共享相同的物理内存页,当父进程处理写请求时会把要修改的页建立副本,而子进程在fork操做过程当中会共享父进程的内存快照。操作系统
触发机制:
1. 手动触发
包括save和bgsave命令。
由于save会阻塞当前Redis节点,因此,Redis内部全部涉及RDB持久化的的操做都经过bgsave方式,save方式已废弃。
2. 自动触发
1> 使用save的相关配置。
2> 从节点执行全量复制操做。
3> 执行debug reload命令。
4> 执行shutdown命令时,若是没有开启AOF持久化功能则会自动执行bgsave。
RDB的优缺点:
优势:
1. RDB是一个紧凑压缩的二进制文件,表明Redis在某个时间点上的数据快照,适合备份,全量复制等场景。
2. 加载RDB恢复数据远远快于AOF的方式。
缺点:
没办法作到实时持久化/秒级持久化,由于bgsave每次运行都要执行fork操做建立子进程,属于重量级操做,频繁执行成本太高。
RDB的相关参数
save 900 1
save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir ./
其中,前三个参数的含义是,
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed # after 60 sec if at least 10000 keys changed
若是要禁用RDB的自动触发,可注销这三个参数,或者设置save ""。
stop-writes-on-bgsave-error:在开启RDB且最近一次bgsave执行失败的状况下,若是该参数为yes,则Redis会阻止客户端的写入,直到bgsave执行成功。
rdbcompression:使用LZF算法压缩字符对象。
rdbchecksum:从RDB V5开始,在保存RDB文件时,会在文件末尾添加CRC64校验和,这样,能较容易的判断文件是否被损坏。但同时,对于带有校验和的RDB文件的保存和加载,会有10%的性能损耗。
dbfilename: RDB文件名。
dir:RDB文件保存的目录。
RDB的相关变量
127.0.0.1:6379> info Persistence
# Persistence
loading:0 rdb_changes_since_last_save:0 rdb_bgsave_in_progress:0 rdb_last_save_time:1538447605 rdb_last_bgsave_status:ok rdb_last_bgsave_time_sec:0 rdb_current_bgsave_time_sec:-1 rdb_last_cow_size:155648
其含义以下:
loading: Flag indicating if the load of a dump file is on-going。是否在加载RDB文件
rdb_changes_since_last_save: Number of changes since the last dump。
rdb_bgsave_in_progress: Flag indicating a RDB save is on-going。是否在执行bgsave操做。
rdb_last_save_time: Epoch-based timestamp of last successful RDB save。最近一次bgsave操做时的时间戳。
rdb_last_bgsave_status: Status of the last RDB save operation。最近一次bgsave是否执行成功。
rdb_last_bgsave_time_sec: Duration of the last RDB save operation in seconds。最近一次bgsave操做花费的时间。
rdb_current_bgsave_time_sec: Duration of the on-going RDB save operation if any。当前bgsave操做已经执行的时间。
rdb_last_cow_size: The size in bytes of copy-on-write allocations during the last RBD save operation。COW的大小。指的是父进程与子进程相比执行了多少修改,包括读取缓冲区,写入缓冲区,数据修改等。
AOF
与RDB不同的是,AOF记录的是命令,而不是数据。须要注意的是,其保存的是Redis Protocol,而不是直接的Redis命令。可是以文本格式保存。
如何开启AOF
只需将appendonly设置为yes就行。
AOF的工做流程:
1. 全部的写入命令追加到aof_buf缓冲区中。
2. AOF会根据对应的策略向磁盘作同步操做。刷盘策略由appendfsync参数决定。
3. 按期对AOF文件进行重写。重写策略由auto-aof-rewrite-percentage,auto-aof-rewrite-min-size两个参数决定。
appendfsync参数有以下取值:
no: don't fsync, just let the OS flush the data when it wants. Faster. 只调用系统write操做,不对AOF文件作fsync操做,同步硬盘操做由操做系统负责,一般同步周期最长为30s。
always: fsync after every write to the append only log. Slow, Safest. 命令写入到aof_buf后,会调用系统fsync操做同步到文件中。
everysec: fsync only one time every second. Compromise. 只调用系统write操做,fsync同步文件操做由专门进程每秒调用一次。
默认值为everysec,也是建议值。
重写机制
为何要重写?重写后能够加快节点启动时的加载时间。
重写后的文件为何能够变小?
1. 进程内超时的数据不用再写入到AOF文件中。
2. 存在删除命令。
3. 多条写命令能够合并为一个。
重写条件:
1. 手动触发
直接调用bgrewriteaof命令。
2. 自动触发。
与auto-aof-rewrite-percentage,auto-aof-rewrite-min-size两个参数有关。
触发条件,aof_current_size > auto-aof-rewrite-min-size 而且 (aof_current_size - aof_base_size) / aof_base_size >= auto-aof-rewrite-percentage。
其中,aof_current_size是当前AOF文件大小,aof_base_size 是上一次重写后AOF文件的大小,这两部分的信息可从info Persistence处获取。
AOF重写的流程。
1. 执行AOF重写请求。
若是当前进程正在执行bgsave操做,重写命令会等待bgsave执行完后再执行。
2. 父进程执行fork建立子进程。
3. fork操做完成后,主进程会继续响应其它命令。全部修改命令依然会写入到aof_buf中,并根据appendfsync策略持久化到AOF文件中。
4. 因fork操做运用的是写时复制技术,因此子进程只能共享fork操做时的内存数据,对于fork操做后,生成的数据,主进程会单独开辟一块aof_rewrite_buf保存。
5. 子进程根据内存快照,按照命令合并规则写入到新的AOF文件中。每次批量写入磁盘的数据量由aof-rewrite-incremental-fsync参数控制,默认为32M,避免单次刷盘数据过多形成硬盘阻塞。
6. 新AOF文件写入完成后,子进程发送信号给父进程,父进程更新统计信息。
7. 父进程将aof_rewrite_buf(AOF重写缓冲区)的数据写入到新的AOF文件中。
8. 使用新AOF文件替换老文件,完成AOF重写。
实际上,当Redis节点执行完一个命令后,它会同时将这个写命令发送到AOF缓冲区和AOF重写缓冲区。
Redis经过AOF文件还原数据库的流程。
1. 建立一个不带网络链接的伪客户端。由于Redis的命令只能在客户端上下文中执行。
2. 从AOF文件中分析并读取一条命令。
3. 使用伪客户端执行该命令。
4. 反复执行步骤2,3,直到AOF文件中的全部命令都被处理完。
注意:AOF的持久化也可能会形成阻塞。
AOF经常使用的持久化策略是everysec,在这种策略下,fsync同步文件操做由专门线程每秒调用一次。当系统磁盘较忙时,会形成Redis主线程阻塞。
1. 主线程负责写入AOF缓冲区。
2. AOF线程负责每秒执行一次同步磁盘操做,并记录最近一次同步时间。
3. 主线程负责对比上次AOF同步时间。
1> 若是距上次同步成功时间在2s内,主线程直接返回。
2> 若是距上次同步成功时间超过2s,主线程会阻塞,直到同步操做完成。每出现一次阻塞,info Persistence中aof_delayed_fsync的值都会加1。
因此,使用everysec策略最多会丢失2s数据,而不是1s。
AOF的相关变量
127.0.0.1:6379> info Persistence
# Persistence
...
aof_enabled:1 aof_rewrite_in_progress:0 aof_rewrite_scheduled:0 aof_last_rewrite_time_sec:-1 aof_current_rewrite_time_sec:-1 aof_last_bgrewrite_status:ok aof_last_write_status:ok aof_last_cow_size:0 aof_current_size:19276803 aof_base_size:19276803 aof_pending_rewrite:0 aof_buffer_length:0 aof_rewrite_buffer_length:0 aof_pending_bio_fsync:0 aof_delayed_fsync:0
其含义以下,
aof_enabled: Flag indicating AOF logging is activated. 是否开启AOF
aof_rewrite_in_progress: Flag indicating a AOF rewrite operation is on-going. 是否在进行AOF的重写操做。
aof_rewrite_scheduled: Flag indicating an AOF rewrite operation will be scheduled once the on-going RDB save is complete. 是否有AOF操做等待执行。
aof_last_rewrite_time_sec: Duration of the last AOF rewrite operation in seconds. 最近一次AOF重写操做消耗的时间。
aof_current_rewrite_time_sec: Duration of the on-going AOF rewrite operation if any. 当前正在执行的AOF操做已经消耗的时间。
aof_last_bgrewrite_status: Status of the last AOF rewrite operation. 最近一次AOF重写操做是否执行成功。
aof_last_write_status: Status of the last write operation to the AOF. 最近一次追加操做是否执行成功。
aof_last_cow_size: The size in bytes of copy-on-write allocations during the last AOF rewrite operation. 在执行AOF重写期间,分配给COW的大小。
若是开启了AOF,还会增长如下变量
aof_current_size: AOF current file size. AOF的当前大小。
aof_base_size: AOF file size on latest startup or rewrite. 最近一次重写后AOF的大小。
aof_pending_rewrite: Flag indicating an AOF rewrite operation will be scheduled once the on-going RDB save is complete.是否有AOF操做在等待执行。
aof_buffer_length: Size of the AOF buffer. AOF buffer的大小
aof_rewrite_buffer_length: Size of the AOF rewrite buffer. AOF重写buffer的大小。
aof_pending_bio_fsync: Number of fsync pending jobs in background I/O queue. 在等待执行的fsync操做的数量。
aof_delayed_fsync: Delayed fsync counter. Fsync操做延迟执行的次数。
若是一个load操做在进行,还会增长如下变量
loading_start_time: Epoch-based timestamp of the start of the load operation. Load操做开始的时间。
loading_total_bytes: Total file size. 文件的大小。
loading_loaded_bytes: Number of bytes already loaded.已经加载的文件的大小。
loading_loaded_perc: Same value expressed as a percentage. 已经加载的比例。
loading_eta_seconds: ETA in seconds for the load to be complete. 预计多久加载完毕。
AOF的相关参数
appendonly yes
appendfilename "appendonly.aof" appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-load-truncated yes aof-use-rdb-preamble no
其中,
no-appendfsync-on-rewrite:在执行bgsave或bgrewriteaof操做时,不调用fsync()操做,此时,Redis的持久化策略至关于"appendfsync none"。
aof-load-truncated:在Redis节点启动的时候,若是发现AOF文件已经损坏了,其处理逻辑与该参数的设置有关,若为yes,则会忽略掉错误,尽量加载较多的数据,若为no,则会直接报错退出。默认为yes。须要注意的是,该参数只适用于Redis启动阶段,若是在Redis运行过程当中,发现AOF文件corrupted,Redis会直接报错退出。
aof-use-rdb-preamble:是否启用Redis 4.x提供的AOF+RDB的混合持久化方案,若为yes,在重写AOF文件时,Redis会将数据以RDB的格式做为AOF文件的开始部分。在重写以后,Redis会继续以AOF格式持久化写入操做。默认值为no。
参考:
1. 《Redis开发与运维》
2. 《Redis设计与实现》
3. 《Redis 4.X Cookbook》