AOF持久化
AOF,Append Only File,是通过保存服务器执行的写命令来记录数据库状态的;被写入AOF文件的所有写命令都是以Redis的命令请求协议格式保存的,因为Redis的命令请求协议是纯文本格式,因此可以直接打开一个AOF文件,观察里面内容;
服务器启动时,会自动载入并且执行AOF文件中保存的写命令,通过这种方式还原服务器关闭之前的数据库状态;DB Loaded from append only file...
1. AOF持久化的实现
AOF持久化功能的实现可以分为命令追加、文件写入、文件同步三个步骤;
1.1 命令追加
当AOF持久化功能处于开启状态时,服务器每执行完一个写命令,都会以Redis命令请求协议的格式追加到redisServer结构的aof_buf缓冲区的末尾;
1 struct redisServer{ 2 // AOF 缓冲区 3 sds aof_buf; 4 }
1.2 AOF文件的写入与同步
Redis服务器进程本质上是一个事件循环,循环中包含文件事件和时间事件;文件事件负责执行客户端发送的命令请求,并且向客户端发送命令回复;时间事件则负责执行类似于serverCron这种定时运行的函数;
服务器在执行文件事件的过程中,可能会执行写命令,改变数据库状态,这些写命令会被追加到aof_buf缓冲区中,所以服务器每次结束一个事件循环时,都会执行flushAppendOnlyFile函数,考虑是否需要将缓冲区中的内容写入并保存到AOF文件里面;
1 def eventLoop{ 2 while true: 3 4 # 处理文件事件; 5 processFileEvents(); 6 7 # 处理时间事件; 8 processTimeEvents(); 9 10 # 考虑是否需要将aof_buf缓冲区中的内容写入和保存到AOF文件中 11 flushAppendOnlyFile(); 12 }
flushAppendOnlyFile()函数的行为由服务器配置文件中的appendfsync选项的值来决定,具体情况如下表所示:
| appendfsync选项的值 | flushAppendOnlyFile函数的行为 |
| always | 每一次事件循环都会将aof_buf缓冲区中的内容写入并同步到AOF文件中 |
| everysec | 每一次事件循环都会将aof_buf缓冲区中的内容写入到AOF文件,如果距离上一次同步间隔超过一秒钟,则会对AOF文件进行同步操作,同步操作会有专门线程负责执行; |
| no | 每一次事件循环都会将aof_buf缓冲区中的内容写入到AOF文件,但是何时同步则有操作系统来决定; |
appendfsync的默认值为everysec;
**文件的写入和同步**
为了提高文件的写入效率,在现代操作系统中,当调用write()函数时,首先操作系统会将数据暂时保存到一个内存缓冲区里面,等到缓冲区被填满后,或者超过指定的时限后,才会将缓冲区中的数据写入到磁盘里面;
这种做法虽然提高效率,但是也带来了安全问题;如果主机发生停机,则内存缓冲区中的数据将会丢失;因此操作系统又提供了fsync和fdatasync两个同步函数,可以强制让操作系统将缓冲区中的数据写入到硬盘里面,从而确保数据的安全性;
AOF持久化的效率和安全性
appendfsync的值为always时,服务器在每一次事件循环的末尾都会将缓冲区中的所有内容写入并同步到AOF文件,所以always的效率是三个选项中最慢的,但安全性也是最高的,即使出现故障停机,AOF持久化也只会丢失一个事件循环中所产生的数据;
appendfsync的值为everysec时,每隔一秒才会通过同步线程将aof_buf中的数据同步到AOF文件中,从效率上来讲,everysec也很高,并且即使出现故障停机,数据库也只会丢失一秒钟的数据;
appendfsync的值为no时,flushAppendOnlyFile()函数不需要执行同步操作,所以AOF文件的写入效率最快,但是同步时间则由操作系统来决定,通常单次同步时长会是三种模式中时间最长的;并且no的安全性也最低,服务器一旦出现故障停机,则会丢失自上次同步后的所有写命令数据;
2. AOF文件的载入与数据还原
Redis读取AOF文件并还原数据库状态的详细步骤如下:
1). 创建一个不带网络连接的伪客户端,因为AOF文件中的写命令只能在客户端的上下文环境中执行,并且执行的写命令都是来自于本地的AOF文件而不是网络连接,因此服务器只需要创建一个不带网络连接的伪客户端即可;
2). 从AOF文件中分析并且读取出一条写命令;
3). 使用伪客户端执行读出的写命令;
4). 重复步骤2)和3),直到AOF文件中的所有写命令都处理完毕为止;
3. AOF重写
随着时间的流逝,AOF文件中记载的写命令会越来越多,文件的体积也会逐渐增大;如果不加以控制,会对宿主机产生严重的影响,此外,AOF文件的体积越大,使用AOF文件还原数据库状态所需的时间也会越多;
AOF重写功能则可以使Redis服务器创建一个新的AOF文件来替换旧的AOF文件,新旧AOF文件保存的数据库状态完全相同,只是新的AOF文件不会浪费空间去保存冗余命令,所以新的AOF文件体积通常比旧的AOF文件体积小得多;
3.1 AOF文件重写的实现
AOF文件重写并不需要对现有的AOF文件进行任何读取、分析和写入操作,而是通过读取服务器当前的数据库状态来实现的;首先从数据库中读取键值对,然后使用写命令去记录该键值对,这既是AOF重写功能的实现原理;
**注意**
为了避免在执行命令时,会造成客户端输入缓冲区的溢出,重写程序在处理列表、哈希表、集合、有序集合这种可能包含多个元素的键时,首先会检查键所包含的元素数量,如果元素数量超过了64,则重写程序会使用多条命令来记录键值,不单单使用一条命令。
3.2 AOF后台重写
AOF重写过程包含了大量的写入操作,由于Redis服务器使用单线程来处理命令请求,因此如果是服务器进程来执行重写任务的话,则在重写期间,服务器无法正常处理客户端发送的命令请求;因此,Redis决定使用子进程去执行AOF的重写工作,这样做有两个好处:
1). 使用子进程执行重写任务,不会影响父进程继续处理客户端发送的命令请求;
2). 子进程会存在一份父进程的数据副本,可以在不使用锁的情况下,保证数据的安全性;
但是也会引发一些问题,子进程在执行重写任务的过程中,父进程执行写命令,导致数据库状态发生变化,从而使得重写的AOF文件保存的数据库状态和服务器当前的数据库状态不一致;为了解决这种数据不一致的问题,Redis服务器设置了一个AOF重写缓冲区,该缓冲区会在子进程执行重写任务时开始使用,在此期间,服务器执行的所有写命令,除了添加到aof_buf中外,还会添加到AOF重写缓冲区中;当子进程完成AOF重写工作后,会向父进程发送一个信号,父进程接收到信号后,会执行信号处理函数,执行以下工作:
1). 将AOF重写缓冲区中的所有内容添加到新的AOF文件中,此时AOF文件记录的数据库状态和服务器当前的数据库状态会保持一致;
2). 对新的AOF文件进行改名,原子地覆盖现有的AOF文件;
父进程在处理信号处理函数的过程中会处于阻塞状态,当信号处理函数执行完毕后,父进程则可以像往常一样接受命令请求;
浙公网安备 33010602011771号