一、Redis持久化概述
Redis是内存数据库,为了避免进程退出导致的数据丢失,Redis会定期的以某种形式将数据从内存保存到硬盘;在下次Redis重启时,利用持久化文件实现数据恢复。
Redis持久化分为RDB持久化和AOF持久化:前者将当前数据保存到硬盘,后者则将每次执行的写命令保存到硬盘(类似于MySQL的binlog);由于AOF持久化的实时性更好,当进程意外退出时,丢失的数据更好,所以AOF是目前主流的持久化方式。
二、RDB持久化
将当前进程中的数据生成快照保存到硬盘,保存的文件名后缀是rdb;当Redis重新启动时,可以读取快照文件恢复数据。
1.触发条件
RDB触发分为手动触发和自动触发
1)手动触发
save命令和bgsave命令都可以生成RDB文件
save命令会阻塞Redis服务器进程,直到RDB文件创建完毕,在此期间,Redis服务器不能接受客户端的响应。
bgsave会创建一个子进程,由子进程负责创建RDB文件,父进程继续处理请求。
注意:目前save已经被基本废除
2)自动触发
save m n
自动触发最常见的情况是在配置文件中通过save m n,指定m秒内发生n次变化时,会触发bgsave
配置文件时redis.conf
save m n的实现原理
Redis的save m n是通过serverCron函数、dirty计数器、和lastsave时间戳实现的。
serverCron是Redis服务器的周期性操作函数,默认是每隔100ms执行一次;该函数对服务器的状态进行维护,其中一项工作就是检查save m n配置的条件是否满足,如果满足就执行bgsave
dirty计数器是Redis服务器维持的一个状态,记录了上一次执行bgsave/save命令后,服务器状态进行了多少次修改;当save/bgsave执行完成后,dirty会重新置0
注意:这里是记录服务器进行了多少次修改,并不是看客户端执行多少修改的命令。
lastsave时间戳也是Redis服务器维持的一个状态,记录的是上一次成功执行save/bgsave 的时间。
其他自动触发机制
(1)主从复制场景下,如果从节点执行全量复制操作,主节点会执行bgsave命令,并将rdb文件发送给从节点
(2)执行shutdown命令时,自动执行rdb持久化
2.执行流程
(1)第一步是Redis父进程先进行判断:当前是否正在执行save或bgsave/bgrewriteaof的子进程,如果是的话,就直接返回。因为基于性能方面考虑,两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。
(2)父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行客户端的任何命令
(3)父进程fork以后,子进程返回消息给主进程,并不再阻塞主进程,主进程可以响应其他命令
(4)子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换。
(5)子进程发送信号给父进程表示完成,父进程更新统计。
3.RDB文件
RDB文件是压缩过的二进制文件
存储路径
RDB文件的存储路径可以在启动前配置,也可以通过命令动态设定
在redis.conf配置文件中 dir:目录位置 dbfilename:文件名
动态设定:config set dir和config set dbfilename
RDB文件格式
各个字段的说明如下:
- REDIS:常量,保存着“REDIS”5个字符
- db_version:RDB文件的版本号,不是Redis版本号
- SELECTDB 0 pairs:表示一个完整的数据库(0号数据库);只有当数据库有键值对时,RDB文件才会有数据库的信息;SELECTDB是一个常量,代表后面跟着的是数据库号码;0和3时数据库号码;pairs存储了具体的键值对信息,包括key、value,及其数据类型等。
- EOF:常量,标志RDB文件正文内容结束
- check_sum:前面所有内容的校验和;Redis在载入RDB文件时,会计算前面的校验和并与check_sum比较
压缩
Redis默认采用LZF算法对RDB文件进行压缩。虽然耗时,但是可以大大减小RDB文件的体积,因此压缩默认开启。可以通过命令关闭:config set rdbcompression no
注意:RDB文件压缩并不是针对整个文件进行的,而是对数据库的字符串进行的,只有在字符串达到一定长度时才会进行。
4.启动时加载
RDB文件的加载是在服务器启动时自动执行的,AOF文件的优先级更高,如果AOF开启时,Redis会优先加载AOF文件恢复数据;只有当AOF关闭时,才会在Redis服务器启动时检测RDB文件,并自动载入。服务器载入RDB文件时处于阻塞状态。
三、AOF持久化
RDB持久化是将进程数据写入文件,而AOF持久化,则是将Redis执行的每次命令都写到单独的日志文件中。
1.开启AOF
Redis服务器默认开启RDB,关闭AOF。
可以在配置文件中配置:appendonly yes
2.执行流程
需要记录Redis的每条命令,因此AOF不需要触发。
- 命令追加(append):将Redis的写命令追加到缓冲区aof_buf;
- 文件写入(write)和文件同步(sync):根据不同的同步策略将aof_buf中的内容同步到硬盘
- 文件重写(rewrite):定期重写AOF文件,达到压缩的目的
1)命令追加
Redis先将写命令追加到缓冲区,而不是直接写入文件,主要是避免每次有些命令都直接写入硬盘,导致硬盘IO称为Redis负载的瓶颈
命令追加是Redis命令请求的协议格式,是一种纯文本格式,兼容性好,可读性强,容易处理;
2)文件写入和文件同步
Redis提供了多种AOF缓存区的同步文件策略,策略涉及到操作系统的write函数和fsync函数。
AOF缓存区的同步文件策略由参数appendfsync控制,各个值含义如下:
- always:命令写入aof_buf后立即调用fsync同步到AOF文件。这种情况下硬盘IO称为性能的瓶颈,Redis只能支持大约击败TPS写入。
- no:命令写入aof_buf中不对AOF文件做fsync同步;同步由操作系统负责,通常同步周期为30s。
- everysec:写入aof_buf后由专门线程每s调用一次,是前面两个策略的折中,是性能和数据安全性的平衡。
3)文件重写
AOF文件太大不仅会影响服务器的正常使用,也会导致数据恢复需要的时间过长。
文件重写指定期重写AOF文件,减小AOF文件的体积。AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF文件中,不会对旧的AOF文件进行任何读取,写入操作。
文件重写能压缩AOF文件原因
- 过期的数据不再写入文件
- 无效的命令不在写入文件:如有血数据被重复设置,有些数据被删除等
- 多条命令可以合并为一个
文件重写的触发
也是分为手动和自动触发
手动:bgrewriteaof命令,与bgsave类似,都是fork子进程进行相关操作。
自动:根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数,以及aof_current_size和aof_base_size状态确定触发时机。
文件重写的流程
3.启动时加载
在AOF开启时,Redis会优先加载AOF文件来恢复数据,即使AOF文件不存在,也不会加载RDB文件。
会进行文件校验
四、方案选择和常见
1.RDB和AOF的优缺点
RDB持久化
优点:RDB文件紧凑,体积小,网络传输快;恢复速度比AOF快很多。对性能影响相对小。
缺点:RDB文件的致命缺点在于数据快照的持久化方式决定了做不到到实时持久化。此外RDB文件需要满足特定格式,兼容性差。
AOF持久化
与RDB持久化相对应,AOF的优点在于支持秒级持久化、兼容性好,缺点是文件大,恢复速度慢,对性能影响大。
2.fork阻塞:CPU的阻塞
-
面对请求的暴增,需要从库扩容时,Redis内存过大会导致扩容时间太长
-
主机宕机时,切换主机后需要挂在从库,Redis内存过大导致挂在速度太慢
-
持久化过程中的fork操作
父进程通过fork创建子进程;父子进程共享代码段,不共享进程的数据空间,但子进程会获得父进程的数据空间副本。虽然fork时子进程不会复制父进程的数据空间,但是会复制内存页表,内存越大复制耗时越多
3.AOF追加阻塞:硬盘阻塞
AOF缓冲区的文件同步策略为everysec,则:主线程中,命令写入aof_buf后调用系统write操作,write完成后主线程返回;fsync同步文件操作由专门的文件同步线程每秒调用一次。
如果硬盘负载过高,fsync可能会超过1s;如果此时Redis进程异常退出,丢失的数据会远超过1s。
为此,Redis处理策略:主线程每次进行AOF会对比上次fsync成功的时间;如果距上次不到2s,主线程直接返回;如果超过2s,主线程阻塞直到fsync同步完成。