为什么很多人认为redis并不能称之为带固化的数据库呢,今天让我们深入了解下redis的两种数据固化方式
1. RDB
RDB快照有两种触发方式,手动方式与自动方式
手动方式(save):由工作线程完成,会阻塞当前的redis服务,直到RDB过程完成为止,这种方式显然不推荐使用了
自动方式(bgsave):fork子进程,持久化过程由子进程负责,完成后自动结束,这种方式有个配置项,save m n
, m秒存在n次写入,则触发bgsave(之所以说redis不能称之为带固化数据库,从这里就可以看出,数据不可靠,这个期间是有可能会丢数据的,不像MySQL,写入成功之后就不存在丢数据问题)
RDB快照方式有什么优缺点呢?
优点:
- 对redis性能影响低(这里指的是bgsave方式)
- 二进制压缩文件体积小
- 数据恢复快
缺点:
- 存在数据丢失可能
- 备份文件版本存在兼容问题
我们看下RDB快照方式的执行流程,如下图:
首先客户端往redis set数据,数据到redis之后进行数据写入操作,此时还会有一个持久化阈值判断,也就是我们上面说到的 save m n
判断m秒之内是否有n次写入,如果达到标准,则会fork一个子进程来完成快照工作,从图中我们可以看到,子进程写的是tmpFile,并不是dump.db,这是因为如果直接写dump.db文件的话,如果写入失败,就会破坏dump.db文件,导致redis不能通过dump.db文件进行恢复了,写tmpFile,写入成功后覆盖dump.db,如果写入失败,也不会影响快照。
2. AOF
AOF(append only file)是以日志形式记录写入指令,实时持久化,我们看下工作流程:
客户端set key,redis开始AOF持久化操作,并写入数据,这个持久化并非实时(是根据配置来的,下面表格整理了下几个配置项),将命令写入到aof_buf
,aof_buf会刷盘到aof文件里进行持久化。
配置 | 含义 | 效率 | 数据安全 |
---|---|---|---|
appendfsync=always | 每次同步 | 低 | 安全最高,最多丢失一个写入数据 |
appendfsync=everysec | 每秒同步 | 中 | 安全折中,最多丢失一秒的数据 |
appendsfync=no | 关闭 | 高 | 安全最低,最多上一次保存AOF文件到当前时刻的全部数据 |
(我们使用redis一般都是比较注重性能的,一般不会选择每次同步,所以说,redis无论哪种方式,数据都会丢,不可靠)
因为aof是日志形式,将命令全部记录,这将会导致这个文件越来越大,因为文件里可能存在很多无效数据,比如我有一个key,进行了如下一连串操作set key -> update key -> update key -> del key,这几个指令都会被记录到aof文件,但是几条指令下来重归于零,等于啥也没干,感觉这种情况就不应该记录了,反正记了也白记,这个时候可能有些小伙伴就想到了LSM 的文件聚合操作,没错,其实redis也是这么干的,会有个文件重写,文件重写的整理方式有以下几个
- 过期数据删除
- 重复更新的数据保留最后一条
- 已经删除的数据进行删除
- 命令聚合
这样就能将aof文件减小,相当于做了一波压缩
文件重写操作也有两种触发方式
- 手动触发:调用bgrewriteaof(很少人会采用手动触发方式)
- 被动触发:被动触发有几个几个参数
- auto-aof-rewrite-min-size (触发重写的最小值)
- auto-aof-rewrite-percentage(与上一次压缩完成后的文件增长比例)
- aof_currenct_size(当前文件大小)
- aof_base_size(上一次压缩完成后的大小)
触发文件重写的条件(当前AOF size > auto-aof-rewrite-min-size) && ((当前AOF size - aof_base_size) / aof_base >= auto-aof-rewritepercentage)
图中我们可以看到,redis子进程开了双写,目的其实就是为了保证不丢数据,因为文件重写不一定成功,如果没有开启双写,指令没有写入aof_buf那么当文件重写失败了,期间的数据就都丢了
redis启动流程:
可以看出当同时开启RDB与AOF时,重启redis是会优先使用AOF方式恢复数据的,这里有点需要特别注意,如果开启了AOF,想使用RDB去恢复数据,此时重启往往不能按照预想的使用RDB文件恢复,反而会加载AOF启动,启动之后又会dump新的RDB,导致之前的RDB文件被覆盖,这点需要特别特别注意,正确操作应该是先关闭aof,启动后再开启aof。
今天分析到此结束,明天继续更新,redis的可靠性保证方案