写在前面除了RDB持久化外,Redis还提供了AOF(Append Only File)持久化功能。 - RDB主要通过保存数据库中的键值对来记录数据库状态不同,也就是快照。
- AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库的状态。
AOF保存过程这篇文章我们就来讲讲AOF持久化的细节原理。 1. AOF持久化的实现AOF 持久化功能的实验可以分为命令追加、文件写入、文件同步 三个步骤
1.1 命令追加当AOF持久化功能启动时,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的 aof_buf 缓冲区的末尾 。 struct redisServer{ // ... // AOF缓冲区 sds aof_buf; // ... };
举个例子:当客户端向服务器发送以下命令 redis>SET FanOne 1 OK
在服务器执行完这个SET命令后,会将以下协议内容追加到 aof_buf 缓冲区 中的末尾: *3\r\n$3\r\nSET\r\n$6\r\nFanOne\r\n$1\r\n1\r\n
aof_buf追加过程1.2 AOF文件的写入与同步服务器会将命令先写入 aof_buf,再根据不同的appendfsync的值来写入AOF文件中。 aof同步过程 | |
---|
| 将aof_buf缓冲区中的所有内容实时写入到AOF文件中并完成磁盘同步 | | 每隔1s将aof_buf缓冲区中所有的内容写到AOF文件中并完成磁盘同步 | | 将 aof_buf 缓冲区中的所有内容写入AOF文件中,何时同步磁盘由操作系统决定 |
三种appendfsync的区别可能有同学会对这里的fsync()存在疑惑,这里我们要注意一点: 一般来说,为了提高文件的写入效率,在现代操作系统中,当用户调用write函数,将一些数据写入到文件的时候,操作系统通过会将写入数据暂时保存在一个内存缓冲区中,等缓冲区的空间被填满或者超过了指定的时限后,才真正的将缓冲区中的数据写入到磁盘里面。 这种做法虽然提高了效率,但也为写入数据带来了安全问题,因为如果机器发生停机,那么保存在内存缓冲区里面的写入数据就会丢失。为此,系统提供了fsync和fdatasync 两个同步函数,可以强制让系统立刻将缓冲区中的数据写入磁盘中。
1.3 AOF 文件的载入与数据还原因为AOF文件里面包含了重建数据库状态的所有写命令,所以服务器只要读入并重写执行一次AOF文件里面保存的写命令,就可以还原服务器关闭之前的数据库状态 。Redis 读取AOF文件并还原数据库状态的步骤如下:
AOF加载过程2. AOF 重写2.1 重写原理因为AOF持久化是通过保存被执行的写命令来记录数据库状态的,所以随着服务器运行时间的流逝,AOF文件中的内容会越来越多,文件的体积也会越来越大 ,如果不加以控制,体积过大的AOF文件会自身的机器实实例造成影响。 举个例子: redis> SET FanOne 1 // FanOne:1 redis> SET FanOne 2 // FanOne:2 redis> SET FanOne 3 // FanOne:3 redis> SET FanOne 4 // FanOne:4
那么记录FanOne这个key和对应的value就要4条命令。 为了解决这个问题,Redis 提供了AOF文件重写(rewrite) 功能 。这样Redis服务器可以创建一个新的AOF文件来替代旧的AOF文件,新旧两个AOF文件所保存的数据库状态相同,但是新的AOF文件不会包含任何浪费空间的冗余命令,所以新的AOF文件的体积通常会比旧的AOF文件的体积要小很多。 而重写的逻辑也非常简单:首先从数据库中读取键限制的值,然后用一条命令去记录键值对就可以了
实际上,为了避免在执行命令时造成客户端输入缓冲区溢出 ,重写程序在处理列表、哈希表、集合、有序集合 这四种可能会带有多个元素的键时,会先检查键所包含的元素数量,如果元素的数量超过了规定的阈值,那么重写程序就会用多个命令去记录键值,而不是单个命令。
2.2 后台重写上面所述的aof_rewrite 函数可以很好的完成创建一个新的AOF文件任务,但由于这个函数有大量的io操作,所以调用这个函数的线程将被长时间阻塞。 而redis是单个线程来处理命令请求,所以如果服务器直接调用aof_rewrite函数的话,那么在重写AOF文件的时候,服务器可能就无法处理客户端发来的命令请求。 所以Redis决定将AOF重写放在子进程中执行。 后台重写过程
|