#### AOF
Append Only File
追加文件的方式
把所有执行的命令全部记录下来,history,恢复的之后把这个文件记录的命令全部再去执行一遍
以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(只记录写命令),只允许追加文件但不允许修改文件,Redis启动之初会读取该文件然后重新来构建文件,即redis启动之初会根据该日志文件的内容从前到后执行一遍依赖完成数据的恢复操作
持久化文件名: appendonly.aof
AOF工作流程
-
主线程fork一个子进程出来,主线程继续处理客户端请求,子线程去备份aof文件
-
子进程新进一个aof临时文件【aof.temp】,将旧的aof文件中关于写的命令写到aof.temp中
-
在此期间主进程处理的新的写入命令
- 一遍存入本身的缓存中
- 一边继续追加到旧的AOF文件末尾
-
子进程写入完之后给父进程发送信号
-
父进程接收到信号后
-
将缓存中的命令追加到AOF临时文件中aof.temp
-
重命名临时文件覆盖原先aof文件
-
开始将新的命令追加到新的aof文件中去
-
默认是不开启的
appendonly yes //开启AOF
关闭再开启后
[root@9cad41dab7ad bin]# ls
appendonly.aof redis-benchmark redis-check-rdb redis-sentinel redis.conf
dump.rdb redis-check-aof redis-cli redis-server
[root@9cad41dab7ad bin]#
aof文件里记录的实际上是一条条写的命令
如果aof文件被而已修改破坏之后,便启动不了redis了,怎么来解决
[root@9cad41dab7ad bin]# vi appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
set
$2
k1
$2
v1
"appendonly.aof" [dos] 33L, 139C
[root@9cad41dab7ad bin]# ./redis-cli -p 6379
Could not connect to Redis at 127.0.0.1:6379: Connection refused
not connected>
可以通过redis-check-aof来修复检测aof文件
./redis-check-aof --fix appendonly.aof
[root@9cad41dab7ad bin]# ./redis-check-aof --fix appendonly.aof
0x 87: Expected \r\n, got: 0a63
AOF analyzed: size=152, ok_up_to=110, diff=42
This will shrink the AOF from 152 bytes, with 42 bytes, to 110 bytes
Continue? [y/N]: y
Successfully truncated AOF
[root@9cad41dab7ad bin]#
此时再去启动redis,客户端便可以成功连接了
[root@9cad41dab7ad bin]# ./redis-server redis.conf
4577:C 17 Nov 2020 12:08:59.808 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
4577:C 17 Nov 2020 12:08:59.808 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=4577, just started
4577:C 17 Nov 2020 12:08:59.808 # Configuration loaded
[root@9cad41dab7ad bin]# ./redis-cli -p 6379
127.0.0.1:6379> #连接成功
随笔分类
重写
AOF默认是会无限进行追加,因此文件会越来越大
为了解决AOF文件体积膨胀的问题,Redis提供了AOF重写功能
即当AOF文件达到一定大小时,会开启(fork)一个新进程来将aof文件进行重写
Redis服务器会创建一个新的AOF文件来替代现有的AOF文件
新旧AOF文件所保存的数据库状态是相同的,但是新的AOF文件不会包含任何浪费空间的冗余命令
所以通常体积会较AOF文件小得多
实现
- AOF重写并不需要对原有的AOF文件进行任何的读取、写入、分析等操作,这个功能是通过读取当前的数据库状态来进行实现的
# 假设服务器对键list执行了以下命令s;
127.0.0.1:6379> RPUSH list "A" "B"
(integer) 2
127.0.0.1:6379> RPUSH list "C"
(integer) 3
127.0.0.1:6379> RPUSH list "D" "E"
(integer) 5
127.0.0.1:6379> LPOP list
"A"
127.0.0.1:6379> LPOP list
"B"
127.0.0.1:6379> RPUSH list "F" "G"
(integer) 5
127.0.0.1:6379> LRANGE list 0 -1
1) "C"
2) "D"
3) "E"
4) "F"
5) "G"
127.0.0.1:6379>
也就是说,当前列表键list在数据库中的值便是["C", "D", "E", "F", "G"].
要用尽量少的命令来记录list键的状态,最简单的方式不是去读取和分析原有AOF文件的内容,而是去直接读取该键在当前数据库中保存的值,这样不就可以用一条命令 RPUSH list "C" "D" "E" "F" "G"
代替前面的6条命令?
Redis的AOF重写也是为了避免会导致当前服务器无法处理新的客户请求
所以去实现了后台重写机制,即Redis将AOF的重写程序放到了子进程(后台)去执行
这样处理的好处:
- 子进程进行AOF重写期间,主进程可以继续处理命令请求
- 子进程带有主进程的数据副本,使用子进程而不是线程,可以避免在有锁的情况下,保证数据的安全性
使用子进程进行AOF重写的问题
- 子进程在进行AOF重写期间,服务器进程还要继续处理命令请求,而新的命令可能对现有的数据进行修改,这会让当前数据库的数据和重写后的AOF文件中的数据不一致。
如何修正
- 为了解决这种数据不一致的问题,Redis增加了一个AOF重写缓存,这个缓存在fork出子进程之后开始启用,Redis服务器主进程在执行完写命令之后,会同时将这个写命令追加到AOF缓冲区和AOF重写缓冲区
- 即子进程在执行AOF重写时,主进程需要执行以下三个工作:
- 执行client发来的命令请求;
- 将写命令追加到现有的AOF文件中;
- 将写命令追加到AOF重写缓存中。
效果
- 可以保证:
- AOF缓冲区的内容会定期被写入和同步到AOF文件中,对现有的AOF文件的处理工作会正常进行
- 从创建子进程开始,服务器执行的所有写操作都会被记录到AOF重写缓冲区中;
完成AOF重写之后
- 当子进程完成对AOF文件重写之后,它会向父进程发送一个完成信号,父进程接到该完成信号之后,会调用一个信号处理函数,该函数完成以下工作:
- 将AOF重写缓存中的内容全部写入到新的AOF文件中;这个时候新的AOF文件所保存的数据库状态和服务器当前的数据库状态一致;
- 对新的AOF文件进行改名,原子的覆盖原有的AOF文件;完成新旧两个AOF文件的替换。
- 当这个信号处理函数执行完毕之后,主进程就可以继续像往常一样接收命令请求了。在整个AOF后台重写过程中,只有最后的“主进程写入命令到AOF缓存”和“对新的AOF文件进行改名,覆盖原有的AOF文件。”这两个步骤(信号处理函数执行期间)会造成主进程阻塞,在其他时候,AOF后台重写都不会对主进程造成阻塞,这将AOF重写对性能造成的影响降到最低。
以上,即AOF后台重写,也就是BGREWRITEAOF命令的工作原理。
优点:
# appendfsync always //每次修改都会进行sync,缓慢且安全
appendfsync everysec //每秒执行一次sync,可能会丢失这一秒的数据
# appendfsync no //不进行sync,让os自己来同步数据,速度更快
- 每一次修改都进行同步,文件的完整性会更好!
- 每秒同步一次,可能会丢失疫苗的数据
- 从不同步,效率是最高的
缺点:
- 相对于数据文件而言,aof远远大于rdb,修复速度也慢于rdb
- AOF机制的运行效率慢于RDB(AOF涉及了大量的IO操作),所以我们默认会去选择RDB