1、为什么redis要持久化?
将redis作为数据库来使用;
将redis作为写缓冲(读缓冲不需要持久化);
当redis崩溃(crash)时,可以很快从磁盘恢复数据,否则会引起缓存雪崩;
2、持久化概述
redis支持RDB和AOF两种持久化方式。
redis.conf中可配置RDB持久化文件和AOF持久化文件的位置和文件名。
# 持久化文件位置
dir ./
# 快照文件名
dbfilename dump.rdb
# AOF文件名
appendfilename appendonly.aof
RDB通过快照完成持久化,发生快照的条件如下:
根据配置规则自动快照;
用户执行save或bgsave命令;
执行flushall;
主从复制;
AOF会将redis执行的每一条写命令追加到磁盘文件中。
redis启动后会读取RDB文件。
redis允许同时开启RDB和AOF,此时redis重启后会使用AOF文件恢复数据,因为AOF方式可能丢失的数据更少。
3、RDB方式
3.1、自动快照
默认的redis.conf中有如下配置
# 每900秒有一个或一个以上的键被更改则进行快照,多个save之间是或的关系
save 900 1
save 300 10
save 60 10000
自动快照是异步的,并不会阻塞客户端请求。
3.2、save或bgsave
save命令是同步操作,阻塞所有客户端请求,生产上避免使用。
bgsave后台异步执行,响应客户端请求,执行bgsave会立即返回OK,想知道快照是否完成可以通过lastsave命令查看最后一次快照时间。
3.3、flushall
执行flushall时,只要自动快照条件不为空,无论是够满足自动快照条件,都会进行一次快照。
3.4、快照原理
redis使用fork函数复制一份当前进程(父进程)的副本(子进程);
父进程继续接收并处理客户端请求,而子进程将内存数据写入磁盘中的临时文件;
当子进程写完所有数据后用临时文件替换旧的RDB文件;
在执行fork函数时操作系统(类Unix系统)会使用写时复制(copy-on-write)策略,即fork函数发生的一刻父子进程共享同一内存数据,当父进程要更改其中某片数据时(如执行一个命令),操作系统会将该片数据复制一份以保证子进程的数据不受影响,因此新的RDB文件存储的是执行fork函数一刻的内存数据。
4、AOF方式
AOF会将redis执行的每一条写命令追加到磁盘文件中。
默认情况下redis没有开启AOF,可通过如下配置开启。
appendonly yes
AOF文件中保存的就是redis通信协议的内容。
比如我执行一条set age 10命令,则AOF文件内容如下。
*3
$3
set
$3
age
$2
10
4.1、优化AOF文件
比如执行了两条命令set age 10和set age 12,其实AOF只用保存第二条命令就行了,因为第二条命令会覆盖第一条命令。
如果冗余命令过多,会导致AOF文件过大而内存数据并没有多少,针对这种情况每达到一定条件(见如下配置)时redis会对AOF文件进行重写。
# 当前AOF文件大小超过上一次重写的AOF文件大小的百分之多少时就重写
auto-aof-rewrite-percentage 100
# 允许重写的最小AOF文件大小
auto-aof-rewrite-min-size 64mb
也可以执行bgrewriteaof命令手动重写。
即使bgrewriteaof执行失败,也不会有任何数据丢失,因为旧的AOF文件在bgrewriteaof成功之前不会被修改。
AOF重写过程:
从主进程中fork出子进程,并拿到fork时的AOF文件数据写到一个临时AOF文件中;
在重写过程中,redis收到的命令会同时写到AOF缓冲区和重写缓冲区中,这样保证重写不丢失重写过程中的命令;
重写完成后通知主进程,主进程会将AOF缓冲区中的数据追加到子进程生成的文件中;
redis会原子的将旧文件替换为新文件,并开始将数据写入到新的aof文件上;
4.2、同步磁盘数据
AOF持久化会将命令记录在AOF文件中,但由于操作系统的缓存限制,数据并没有真正写入磁盘,而是进行了系统的磁盘缓存,默认情况下系统每30秒进行一次同步,如果这30秒期间系统异常退出则会导致数据丢失。
可通过如下配置设置同步时机。
# 总是同步
appendfsync always
# 每秒同步一次(默认)
appendfsync everysec
# 由操作系统决定(30秒)
appendfsync no
5、设置了过期时间的key会持久化吗?
如果一个key设置了过期时间,那么它仍然会持久化。
假设一个key的过期时间设置为60秒,然后关闭redis进程,过20秒再启动redis进程,此时用TTL命令查看这个key的剩余时间,应该是40秒,而不是60。
这是因为redis用了一个字典来存储key的过期时间,字典的key为指向redis key的指针,而字典的value为该redis key的到期时间(比如:2021-04-05 18:30:00),而不是剩余时间(比如:剩余60秒),这样即使redis崩溃重启也不会对设置了过期时间的key造成影响。





