redis是一个键值对的内存数据库服务器,它将数据库状态存储在内存之中,但是如果一旦服务器出现问题意外crash或者重启那么内存中的数据库状态将全部丢失,为了解决这个问题,redis提供了持久化方案,将内存中数据库状态同步到磁盘保障数据
redis提供了两种持久化方案:rdb,aof
- rdb方式:将数据库状态(数据库中键值对)保存为一个经过压缩的二进制文件
- aof方式:将数据库写命令保存为一个固定格式的文件
redis-rdb持久化
rdb文件创建
redis通过两个命令来用于生成rdb文件:
- save:
- save命令在生成rdb文件期间会阻塞redis主进程,直到rdb文件创建完成之前redis主进程无法处理任何客户端请求
- 由函数rdb.c/rdbSave函数执行、
- 函数rdbSave本身就会阻塞进程
- bgsave:
- bgsave命令通过由主进程fork出一个子进程来执行生成rdb文件,生成rdb文件期间不会阻塞主进程
- 由函数rdb.c/rdbSave函数执行
- 问题:bgsave命令执行期间服务器如何处理save,bgsave,bgrewriteaof三个命令
- bgsave命令执行期间Redis服务器拒绝执行save命令,防止主进程和子进程同时执行rdbSave函数导致资源竞争
- bgsave命令执行期间Redis服务器拒绝执行bgsave命令,防止两个子进程同时执行rdbSave函数导致资源竞争
- bgsave和bgrewriteaof命令吧態同时执行
- bgsave执行期间bgrewriteaof命令会被延后,直到bgsave执行完成才可以执行bgrewriteaof
- bgrewriteaof执行期间,bgsave命令会被丢弃
rdb文件载入
rdb文件载入实在服务器启动的时候就会自动执行,所以redis并没有专门的用关于rdb文件载入的命令,只要redis服务器检测到rdb文件就会自动载入rdb文件
rdb文件载入期间,主进程处于阻塞状态,直到载入完成才可以执行客户端命令
问题:如果rdb方式和aof方式同时开启,redis服务器如何使用持久化方式恢复的
- 如果aof方式开启,由于aof方式的实时性更高,所以redis会优先使用aof文件来恢复数据库
- 只有aof方式处于关闭状态,redis服务器才会使用rdb文件来做数据库恢复
rdb自动间隔性保存
由于bgsave在执行期间不会阻塞主进程,所以redis允许通过配置服务器配置save属性,让服务器在满足条件的情况下自动还行bgsave
配置放在redis.conf文件中,格式为:save <seconds> <changes>
- 当满足在seconds秒内,redis数据库执行过changes次修改,那么就会触发自动bgsave
- save属性可以设置多个,在设置多个的情况下,满足任意一个就会自动执行
- redis默认配置:
如何触发自动间隔性保存?
- redis的时间事件执行周期性的函数serverCron默认每100ms执行一次扫描。会检查设置的save属性是否满足
- 如果条件满足那么就会执行bgsave,否则继续周期扫描检测
redis-aof持久化
aof的持久化功能实现可以分为:命令追加,文件写入,文件同步三个步骤
- 命令追加:当aof功能处于打开状态,服务器在执行完写命令后,会将这个写命令追加到aof_buf内存缓冲区中
- 文件写入和文件同步:
- redis主进程本身就是一个事件循环event-loop
-
处理文件事件执行写命令,并追加到aof_buf
-
处理时间事件执行周期任务
-
结束事件循环前-调用flushAppendOnlyFile函数考虑将aof_buf内容写入和保存早aof文件中
-
flushAppendOnlyFile的函数行为由服务器配置:appendfsync属性决定,属性值不同则产生不同的行为
-
appendfsync=always:总是将aof_buf缓冲区内容同步写入到aof文件中
- 安全性最高,只会丢失一个事件循环内的数据
- 效率最差,大量的I/O操作
-
appendfsync=everysec:每秒周期的将aof_buf缓冲区同步的写入到aof文件中
- 安全性适中,丢失一秒内的写数据
- 效率适中
-
appendfsync=no:不执行将aof_buf缓冲区内容同步到aof文件的操作
- 安全性最差,丢失自上次之后所有数据
- 效率最高
-
-
aof文件的载入和数据还原
aof文件中包含了重建数据库的所有写口令,所以服务器只需要读入命令并且重新执行一遍aof文件中命令,就可以还原数据库
- 1:创建一个无网络连接的伪客户端:因为redis的命令执行能在客户端执行。所以利用伪客户端来执行aof文件中命令
- 2:从aof文件中分析并读取一条写命令
- 3:使用伪客户端执行读取到的写命令
- 4:循环执行2,3直到完全执行完所有aof文件中命令
aof重写
aof文件是通过记录写命令来实现持久化,但是存在一个问题:对同一个key在给定时间内会发生多次写操作,所以aof文件存在越变越大的问题,而体积过大aof文件对于redis服务器的压力会变大,所以需要解决这一弊端;最好的情况是无论执行多久,对于相同的key在aof文件中只存在一条有效的能够保障当前数据库状态的写命令,所以redis提供了重写功能
重写的实现:
- 触发条件:
- 手动触发:执行bgrewriteaof命令
- 自动触发:配置属性
- auto-aof-rewrite-min-size :aof文件最小重写大小
- auto-aof-rewrite-percentage:当前aof文件大小与最后一次重写后大小比率,100表示aof是上次重写时两倍才重写
- 理论依据:aof重写时通过读取当前服务器数据库状态来实现的
- 通过aof文件中的key从redis数据库中获取key的当前状态并将当前状态装换为写命令来实现
- 实际操作:
- redis提供了aof_write函数来实现redis-aof重写
- aof_write函数在执行期间会阻塞主进程
aof后台重写
由于aof_write在执行期间会阻塞主进程,而aof重写只是一种辅助手段,所以redis不希望重写阻塞主进程,所以提供aof后台重写功能,aof重写由主进程fork出的子进程来执行aof重写,主进程则继续接受和执行客户端命令
问题:aof后台重写期间主进程依旧执行写命令,导致数据状态最终不一致
- redis在aof后台重写期间会开启aof重写缓冲区
- 主进程在aof后台重写期间每执行一条写命令,都会将写命令同时发送到aof缓冲区和aof重写缓冲区
-
最终aof文件重写完毕后,会将aof重写缓冲区文件同步写入新aof文件,保证了最终一致
aof重写步骤
- 开始后台aof重写,主进程fork子进程开始执行bgrewriteaof命令
- 主进程将重写期间的写命令同时发送到aof缓冲求,aof重写缓冲区
- 子进程完成aof重写工作,向主进程发送一个信号
- 主进程接收到信号调用信号处理函数
- 将aof重写缓冲器数据写入新的aof文件
- 重命名新的aof文件并原子覆盖旧aof文件
- 重写完成
如何合理使用持久化
rdb文件紧凑单一但是实时性弱,aof耐久实时性好但是体积会大,基于二者在性能,安全性,实时一致性上的特点,rdb更加适合做冷备份,非常便于数据库备份;aof适合做热备份,适合做容灾。但是小孩子才做选择,在线上生产环境当然是选择两种持久化方式同时开启使用,利用rdb来保存数据库数据副本,利用aof记录实时的命令
redis4.0混合持久化
4.0的新特性,将rdb与aof持久化方式二合一结合了两者的优点,可以快速加载同时避免丢失过多数据,缺点就是文件可读性差
新特性中aof文件会由两部分组成,一半是rdb格式,一半是aof格式
混合持久化过程
- 发生时间点:aof-rewrite被触发
- 如何开启:aof-use-rdb-preamble yes (redis5.0默认开启)
- aof-rewrite流程:
- fork子进程遍历old-aof文件读取数据库状态写入new-aof文件
- 在写new-aof期间新的写命令会记录在重写缓冲区中
- 当写new-aof完成,子进程通知主进程,主进程将重写缓冲区数据写入new-aof文件
- 重命名并原子替换old-aof
- aof-rdb混合持久化:
- fork子进程会以rdb的格式将数据库状态写入new-aof文件
- 在写入new-aof期间新的写命令会记录在重写缓冲区中
- 子进程以rdb格式同步数据库状态ok,通知主进程
- 主进程会将重写缓冲区中增量命令以aof格式写入new-aof文件
- 重命名并原子替换old-aof
- aof-rdb混合持久化加载:
- 加载方式依旧遵循优先aof文件
- 当开启aof并开启混合持久化时优先加载混合aof文件
- 混合aof文件以顺序加载
- 先rdb后aof
- 否则,由aof先aof,否则rdb
总结:
- rdb优点:
- 结构紧凑(二进制压缩文件)体积小,适合容灾恢复
- 恢复大数据集时比aof快 (rdb记录的是数据库键值对信息)
- 可以最大化redis性能(bgsave是fork子进程执行)
- rdb缺点:
- 快照数据,数据完整度不高,适合冷备不适合热备
- 数据量大时,子进程耗费cpu越多,耗时越久
- 文件二进制格式,可读性差
- aof优点:
- 数据完整度高(appendfsync=always丢失一个事件循环的数据)
- 兼容性高(命令追加)
- aof缺点:
- 数据体积大,就算aof-rewrite(命令追加导致)
- aof加载速度比rdb慢(一条条由伪客户端执行命令)