一、概述
1、持久化是什么?
Redis 是跑在内存里的,当程序重启或者服务崩溃,数据就会丢失,如果业务场景希望重启之后数据还在,就需要持久化,即把数据保存到可永久保存的存储设备中
2、持久化方式
Redis提供两种方式来持久化:
- RDB:记录redis某个时刻的全部数据,这种方式本质就是数据快照,直接保存二进制数据到磁盘,后续通过加载RDB文件恢复数据
- AOF:记录执行的每条命令,重启之后通过重放命令来恢复数据,AOF本质是记录操作日志,后续通过日志重放恢复数据
RDB是快照恢复,AOF是日志恢复,这是两者本质区别,但还有另外一些差别:
- 体积方面:相同数据量下,RDB体积更小,因为是二进制紧凑型文件
- 恢复速度:RDB是数据快照,可以直接加载,而AOF文件恢复,相当于重放情况,RDB显然会更快
- 数据完整性:AOF记录了每条日志,RDB是间隔一段时间记录一次,用AOF恢复数据通常更为完整
二、RDB
1、怎么开启持久化?
配置语法:save interval num 表示间隔interval秒,至少有num条写数据操作
save 900 1
save 300 10
save 60 10000
这三条是Redis默认存在的:说明redis默认开启了RDB持久化
2、什么时候持久化?
1、主动执行命令save
127.0.0.1:6379 > save
OK
save命令:主线程生成RDB文件,由于和执行操作命令在同一个线程,所以如果写入RDB文件的时间太长,会阻塞主线程
2、主动执行命令bgsave
127.0.0.1:6379 > bgsave
Background saving started
bgsave命令:创建一个子进程来生成RDB文件,这样可以避免主线程的阻塞
3、达到持久化配置
Redis配置持久化策略 也就是最开始介绍的开启持久化
周期函数会检查是否达到策略,到达就触发bgsave
save 900 1
save 300 10
save 60 10000
4、在程序正常关闭的时候执行
在关闭时,Redis会启动一次阻塞式持久化(save),已记录更全的数据
3、RDB具体做了什么?
三件事:
- Fork出一个子进程专门做RDB持久化
- 子进程写数据到临时的RDB文件
- 写完之后,用新RDB文件替换旧的RDB文件
整体流程结构:
因为是子进程进行save,所以执行RDB持久化过程中,Redis依然可以继续处理操作命令的,也就是数据是能被修改的,这就是写时复制技术实现的
具体而言:fork创建子进程之后,通过写时复制技术,子进程和父进程是共享同一块内存数据的,因为创建子进程的时候,会复制父进程的页表,但是页表指向的物理内存还是一个
只有发生修改内存数据的情况时,物理内存才会被复制一份。
就是这样,Redis使用bgsave对当前内存中的所有数据做快照,这个操作是由bgsave子进程在后台完成的,执行时不会阻塞父进程中的主进程,这就使得主线程可以同时修改数据
这样的目的是为了减少创建子进程时的性能损耗,从而加快创建子线程的速度,毕竟创建子进程的过程中,是可能阻塞主线程的(数据量非常大的时候)
可以看到,复制期间,读数据互相不影响,如果有写操作发生,则主进程复制一份内存,在这个复制的内存基础上,主进程在修改原来的数据,子进程持久化的依然是修改之前的数据
三、AOF
1、怎么开启 AOF
appendonly yes
打开之后,Redis每条更改数据的操作都会记录到AOF文件中
当你重启后,AOF会助你重建状态,相当于请求全部重放一次,所以AOF恢复起来会比较慢
2、AOF写入流程
Redis每条更改数据的操作都会记录到AOF文件中,会影响Redis的执行性能
但Redis实际是提供了不同的策略来选择不同程度的损耗,提供了3种刷盘策略:
- appendonly always:每次请求都刷入AOF;
- appendonly everysec:每秒刷一次盘;
- appendonly no:不主动刷盘,让操作系统自己刷,一般情况下linux会每30秒刷一次
官方建议是 appendonly everysec,也就是每秒刷一次盘,同时崩溃时损失的数据只有1s,这大多数场景都是可以接受的
当然了,我们要根据实际业务来选择,如果不做什么超级热点缓存,就是简单的缓存,丢失30秒也不是什么大事,也可以选择 appendonly on
3、写入AOF细节
写入AOF,其实分了好几步
完整示意图:
第一步:将数据写入AOF缓存中,这个缓存的名字是auf_buf,一个SDS数据
第二步:aof_buf对应数据刷入磁盘缓冲区;那什么时机执行呢?
- 处理完时间处理后,等待下一次事件到来之前
- 周期函数serverCron之后
- 服务器退出之前的准备工作时
- 通过配置指令关闭AOF功能时
第三步:刷盘,即调用系统的flush函数;根据不同的策略刷盘
- 如果是appendfsync alaway策略,则立刻调用redis_fsync刷盘
- 如果是aof_fsync_everysec策略,会用aof_background_fsync使用后台线程刷盘
4、AOF重写
aof是不断写入的,这容易带来一个疑问,如此下去aof不是会不断膨胀吗?
针对这个问题,Redis采用了重写的方式来解决:
Redis可以在aof文件提交变得过大时,自动地在后台fork一个子进程,专门对aof进行重写;针对于相同key的操作,进行合并,比如同一个key的set操作,那就是后面覆盖前面
在重写过程中,Redis不但将新的操作记录原有的aof缓冲区,而且还会记录在aof重写缓存区。一旦新aof文件创建完毕,Redis就会将重写缓冲区内容,追加到新的aof文件,再用新的aof文件替换原来的aof文件
aof达到满足这两个条件则重写:
# 相比上次重写时数据增长100%
auto-aof-rewirte-percentage 100
# 超过
auto-aof-rewirte-size 64mb
在超过64m或相比上次重写时的数据大一倍,则触发重写,很明显,最后实际上还是在周期函数来检查和触发的
四、AOF优化-混合持久化
1、混合持久化是什么?
混合部署听名字似乎是同时开启RDB和AOF,实际上不是的;
混合部署时机发生在AOF重写阶段,将当前状态保存为RDB二进制内容,写入新的AOF文件,再将重写缓冲区的内容追加到新的AOF文件,最后替换原有的AOF文件
此时的aof文件,就不再单纯的是日志数据,而是二进制数据 + 日志数据的混合体,所以叫混合持久化
2、混合持久化解决什么问题?
aof文件内容从日志数据变成一部分体积更小的二进制数据 + 一部分aof日志数据;比常规的aof文件体积要小一些,恢复数据更快
3、怎么开启?
redis.conf
aof-user-rdb-preamble
5.0之后默认打开的,所以5.0之后只要AOF配置开启,默认就是混合持久化
4、如何加载持久化数据
完整的具体加载流程如下: