作者:xz7777 | 来源:互联网 | 2023-09-10 10:23
RDB持久化是通过保存数据库中的键值对来记录数据库的状态,数据库中的状态是数据库中的内容,AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库的状态AOF持久化的实现分
RDB持久化是 通过保存数据库中的键值对来记录数据库的状态,数据库中的状态是 数据库中的内容,AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库的状态
AOF持久化的实现分为命令追加,文件写入,文件同步三个步骤,命令追加,当AOF持久化功能打开后,会以协议的格式将被执行的命令追加到redisServer结构中的aof_buf缓冲区的末尾,文件写入与文件同步,Redis服务器进程就是一个事件循环loop,这个循环中的文件事件负责接收客户端的命令请求,以及向客户端发送命令回复,而时间事件则负责执行像serverCron函数(服务器周期性的执行函数,对正在运行的服务器进行维护,检查save选项所设置的保存条件是否满足)这样需要定时运行的函数,服务器
事件循环中,处理文件事件,接收命令请求,以及发送命令回复,处理命令请求时候如果有写命令将追加到aof_buf缓冲区中,处理时间事件,在一个事件循环结束之前执行flushAppendOnlyFile() 将函数将aof_buf缓冲区中的内容写入和保存到aof文件中。flushAppendOnlyFile()函数的执行内容可以通过服务器的appendfsync选项来设置 ,appendfsync是写入和同步选项,always 将aof_buf缓冲区中的所有内容写入并同步到AOF文件中,everysec 缓冲区中的所有内容写入AOF文件中,如果上次AOF文件同步时间距离现在超过1秒,则再次对AOF文件进行同步,这个同步操作由一个专门的线程负责,no 将aof_buf缓冲区中的所有内容写入到AOF文件中,但是不进行同步,没有主动设置appendfsync的时候默认为everysec,现代的操作系统,当用户调用write函数的时候,将一些数据写入到文件的时候,操作系统通常会将写入的数据暂时保存在一个内存缓冲区中,知道缓冲区满了之后,或者超过了指定的时限之后才真正的将缓冲区中的数据写入到磁盘里面,这种方式提高了效率,但是带来了数据丢失的安全性能,为此以通提供了fsync fdatasync两个同步函数可以强制让操作系统立即将数据写入到磁盘里。
AOF文件中包含重建数据库状态的所有写命令,服务器执行一遍AOF文件就能还原关闭之前的数据库状态
Redis读取AOF文件并还原数据库的状态详细的步骤是:1.创建一个不带网络连接的fake client伪客户端,因为redis命令只能在客户端上下文中执行,而载入AOF文件时候使用的命令直接来源于AOF文件而不是网络连接,所以服务器创建一个没有网络连接的为客户端来执行AOF文件保存的写命令,执行的效果和有网络连接的客户端执行的命令效果一样,2.从AOF文件中分析并读取一条写命令,3.使用伪客户端执行被读出的写命令,一直执行,直到AOF文件中所有的写命令被处理完毕,AOF文件中第一个命令就是select 0 表示选择数据库。随着时间的流逝,AOF文件中的内容越来越多,体积越来越大,对服务器是不好的影响,进行数据还原的时间就越来越多,解决AOF文件体积膨胀问题,AOF文件提供了重写的功能,Redis可以创建一个新的AOF文件,新的AOF文件保存的数据库状态相同,但是新的AOF文件中不包含任何浪费空间的命令,新的AOF文件要小的多。
AOF重写并不是将已有的AOF文件进行重新的写入和执行,而是从数据库中读取键现在的值,然后用一条命令记录键值对代替之前记录这个键值对的多条命令,比如将AOF中多条对同一个键的写操作命令,在新的AOF文件中用一条命令来实现。函数aof_rewrite实现了AOF重写,首先是遍历所有的数据库,在一个数据库中遍历所有的键,过期键不用管,根据键的类型进行重写,get命令获取键的值,set命令重写字符串中的键。AOF重写的aof_write函数不会有没有用的键,并且读取数据库的值达到了多条指令的整合。在AOF重写的过程中可能会出现某一个键的元素过多,造成了客户端输入缓冲区aof_buf溢出,redis先检查键中包含的元素的个数,如果超过了元素的个数,会使用多条命令记录键的值,就是说AOF重写的时候,读取的键的值过多的时候将会在新的AOF文件中使用多条指令。执行aof_rewrite函数的时候有大量的写操作,调用这个函数的线程将会长时间被阻塞,redis服务器是使用单个线程来处理命令请求,因此,如果reids服务器直接执行aof_rewrite函数的时候服务器将不会处理客户端发来的命令请求。Redis将AOF重写放在子进程中,这样子进程在执行AOF重写的时候,服务器进程可以继续处理命令请求,子进程带有服务器进程的数据副本,使用子进程而不是线程在避免使用锁的情况下保证数据的安全性。
子进程带有父进程的数据副本,这样的话,在子进程进行AOF重写的时候,服务器进程还是在继续处理命令,命令可能会改变现有的数据库状态,从而使得当前的数据库状态和后台重写之后保存的数据库状态不一致。解决数据不一致,redis设置了一个AOF重写缓冲区,这个缓冲区在服务器创建子进程之后开始使用,当Redis服务器执行完一个写命令之后,会同时将这个写命令发送给AOF缓冲区和AOF重写缓冲区。redis服务器进程在子进程执行AOF重写的时候会进行三个操作,执行客户端发送来的命令,将执行的写命令追加到AOF缓冲区,将执行的写命令追加到AOF重写缓冲区中,AOF缓冲区中的内容会定期的被写入和同步到AOF文件中,对现有的AOF处理像正常一样,从创建子进程开始,服务器执行的写命令会被记录到AOF重写缓冲区中,子进程完成重写之后向父进程发送一个信号,之后父进程调用信号处理函数,将AOF重写缓冲区中的所有内容写入到新的AOF文件中,这时候AOF文件保存的数据库状态将会服务器当前的数据库状态保持一致,之后对新的AOF文件改名原子性的覆盖现有的AOF文件,完成新旧AOF文件的交替,信号函数执行完毕之后,父进程将会像往常一样接收新的命令请求了,在整个AOF重写的过程中,REDIS服务器只有在执行信号处理函数的时候才会阻塞,这样AOF后台重写对服务器造成的性能影响降到了最低。这就是BGREWAITAOF的实现