Redis虽然是内存数据库,但会把缓存数据保存到硬盘中持久化。使用了AOF和RDB进行持久化。
AOF日志
概括:aof是追加文件,可以在主线程中将写操作的命令追加到磁盘中,需要手动打开,有三种写回硬盘的策略,并且如果AOF文件过大,会重写AOF文件。
AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件。
AOF持久化功能默认是关闭的,需要在redis.conf配置文件中打开。
Redis写入AOF日志的过程:
1. 执行写操作命令。
2. 命令追加到内存中aof缓冲区。
3. I/O系统调用write()方法写入内核缓冲区中。
4. 由内核向硬盘发起写操作,即调用fsync()函数将数据写入磁盘
写回策略
写入硬盘有三种策略:
1. Always:每次写操作命令执行完成后就将AOF日志同步写入磁盘
2. everysec:每次写操作执行完成后,先把命令写入AOF文件中(在内核缓冲区),每隔一秒把AOF写入硬盘。
3. NO:由操作系统决定什么时候写回磁盘。
重写机制
AOF文件如果过大,会进行重写,可以在conf文件中配置阈值触发重写,也可以手动执行bgrewriteaof命令。
因为是记录命令,AOF会随着时间的增长越变越大,会导致文件过大并且在恢复数据会耗时增加,而同一个key常常会记录多个命令,只有最后一个命令才有效,所以可以针对这个情况进行优化。
重写过程:在后台新开一个子进程直接读取当前Redis中每一个键值对,用命令记录在一个新的AOF文件中,然后将新的AOF文件替换掉旧的AOF文件。
这时会出现一个问题,如果主线程接收到了一个新的写操作,要修改已经重写了的key-value,就会导致数据不一致的现象。
所以重写过程采用了写时复制的方式,即子进程复制了主进程的页表,主进程和子进程共享Redis的数据,但只能读不能写。
- 当主进程接收到客户端的写操作时,会复制要修改的那块数据,在复制的数据上面进行修改key-value,并且将写操作命令放在AOF重写缓冲区和AOF缓冲区中。
- 当子进程完成重写AOF日志后,会向主进程发送一个信号,然后主进程就会将AOF重写缓冲区的命令追加到子进程的新的AOF文件中,并且将新的AOF文件替换掉旧的AOF文件。
RDB快照
RDB快照就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,快速恢复恢复数据。
触发条件
-
执行save命令,主进程会被阻塞,不能处理客户端的命令。
-
执行bgsave命令,子进程执行,主进程可以继续处理命令。
-
Redis停机时
-
触发RDB条件时,在conf文件中配置,在多少秒内至少有多少个key被修改。
执行bgsave时主线程可以继续执行命令,因为这里也采用了写时复制,当主进程执行写操作时,则会拷贝一份数据,在副本里执行写操作,所以bgsave得到的是在刚开始的所有数据,主进程新修改的数据在下一次快照才能得到。
AOF与RDB
保存RDB快照的代价较高,需要进行大量CPU操作,所以要执行的频率低,导致容易数据丢失。
执行AOF文件的代价较高,因为AOF保存的是命令,需要一个一个执行。
通常两者结合使用,需要在配置文件中配置aof-use-rdb-preamble yes
具体是在AOF重写时直接用RDB快照记录当前的数据,然后将在执行快照时主进程中的新写操作命令追加到AOF文件中。此时,新的AOF文件包括两部分,前面是完整的RDB内容,后面是新的AOF追加命令。