RDB快照持久化
- Redis使用独立子进程进行快照持久化工作
- Redis是单线程程序,该线程负责监听多个客户端套接字的读写操作和内存数据的维护。快照持久化需要将内存数据全量写入磁盘,涉及大量的IO操作,如果仍然使用主线程进行,则必定阻塞正常请求
- RDB快照是一次全量备份,记录持久化开始那个时刻的内存数据,是内存数据的二进制序列化形式。
- Redis的快照持久化是基于linux系统的
copy-on-write
功能实现的
COW
传统的fork()
传统的创建子进程操作,创建出的子进程和父进程共享代码段,复制数据段、堆、栈,这样会造成大量内存复制;传统fork()的内存复制情况如下图:
支持写时复制的fork()
支持写时复制的创建子进程操作,在子进程被创建的瞬间,内核只为子进程创建虚拟空间,不分配物理内存;父进程和子进程共享物理内存,此时内存分布如图:
当父进程中有更新数据的行为时,才将更新涉及到的数据页拷贝出来独立物理空间给子进程
redis的写时复制
Redis是基于内存的缓存,主线程中有大量内存数据,如果创建子进程时使用传统的fork(),物理内存占用必定加倍,服务器为了支持快照持久化,日常内存使用率只能在50%以下,这显然是不合理的。
所以需要使用操作系统的cow机制来进行父子进程数据页的共享和分离
- 在子进程被创建瞬间,服务器的内存增长几乎忽略不计(仅子进程必要的数据结构),父子进程共享物理内存
- 父进程继续提供服务,子进程则进行内存快照写入磁盘工作
- 当父进程需要修改数据时,则将修改数据涉及的数据页(每个数据页4KB)复制一份进行修改,此时子进程仍然看到快照时的数据
快照持久化配置
RDB持久化配置位于redis.conf
文件中,SNAPSHOTTING对应的内容:
# 快照触发条件,命中某一个条件就会执行一次快照持久化BGSAVE
# 服务器在900s内,对数据库进行了至少1次修改
# 服务器在300s内,对数据库进行了至少10次修改
# 服务器在60s内,对数据库进行了至少10000次修改
save 900 1
save 300 10
save 60 10000
# 快照文件存放路径及文件名
# 文件名默认
# 文件路径默认当前目录下
dbfilename dump.rdb
dir ./
# RDB快照持久化被激活并且持久化失败,Redis则停止接受更新操作
stop-writes-on-bgsave-error yes
# 生成.rdb文件时是否压缩
rdbcompression yes
# RDB快照文件加载时是否进行CRC64校验
rdbchecksum yes
AOF持久化
- AOF日志记录对内存进行修改的指令记录
- Redis对修改指令先进行逻辑处理,再写入AOF日志
fsync
Redis对AOF文件的写操作,是先写入AOF日志文件对应文件描述符的内存缓存中,然后再异步将脏数据刷回磁盘,这种机制导致如果服务器宕机,可能存在内存缓存中的日志记录会丢失;
linux提供了fsync()
函数,可以将指定文件描述符对应的内存缓存强制写入磁盘;但fysnc()是一个耗时久的磁盘IO操作,因此,redis提供了三种执行fsync()的方式:
- redis每隔1s执行一次
fsync()
;redis的默认策略- 这就意味着AOF日志最多可能丢失1s的日志数据
- redis完全不执行
fsync()
,AOF日志落磁盘的操作完全交给操作系统管理- 不可靠
- redis每次执行修改命令,都执行
fsync()
- 耗时较久,相当于每次修改指令都要强制写入磁盘
AOF重写
AOF文件时增量写入的,所以如果长时间不处理AOF文件,其容量巨大,出现问题重新加载会非常耗时,因此
Redis提供了bgrewriteaof
指令执行AOF重写,对AOF文件进行瘦身,其方式大致如下:
- 开辟子进程对内存进行遍历
- 将遍历的内存数据,转换成对应的Redis操作指令
- 将扫描期间增量的日志追加到新的AOF文件后面
- 新的AOF文件替换旧的AOF文件
AOF持久化配置
RDB持久化配置位于redis.conf
文件中,APPEND ONLY MODE对应的内容:
# 是否开启AOF日志,默认不开启,线上一般需要打开 => yes
appendonly no
# AOF日志文件名
appendfilename "appendonly.aof"
# AOF日志执行fsync策略,默认每秒执行一次fsync,还可以
# appendfsync always => 每条修改指定执行一次fsync
appendfsync everysec
# appendfsync no => 不执行fsync
# 当后台执行BGSAVE或者BGREWRITEAOF时,主线程不执行fsync
# 主要考虑到当子进程进行“RDB快照”或者“AOF重写”时,会产生大量I/O,
# 可能影响主线程的fsync()操作,导致阻塞时间增加,redis性能下降;
# 所以,可以设置此时主线程不执行fsync(),
# 但这样也意味着可能会丢失更久的AOF日志数据(最长达30s,根据linux的默认配置)
no-appendfsync-on-rewrite no
# AOF文件重写时机配置
# 当前AOF日志大小 大于auto-aof-rewrite-min-size,且比上次rewrite后(或者启动后)的日志大小增长auto-aof-rewrite-percentage,则触发AOF重写;
# auto-aof-rewrite-percentage 设为0时,关闭AOF重写功能
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 在redis启动并加载AOF日志时,发现从AOF日志不全(truncated),这个时候怎么办呢?
# yes => redis可以正常重启,并会告知用户AOF文件不全
# no => redis重启失败,直接抛错
aof-load-truncated yes
# AOF重写时,是否使用rdb作为AOF日志的基
# yes => 重写时会指定rdb文件,并在此文件上增量生成aof,如果发生重启需要重新加载,也是使用rdb + 增量aof
# 感觉像是混合持久化的开关
aof-use-rdb-preamble yes
混合持久化
RDB快照 + 自RDB快照后的增量AOF日志
可以实现快速全量的数据恢复