深度了解Redis持久化的方式——AOF

目录

AOF文件

AOF持久化的实现

AOF文件的载入与数据还原

AOF重写


上篇文章讲述了Redis的默认持久化方式RDB,接下来介绍一下另一种方式——AOF。

AOF与RDB的不同之处在于,RDB是通过保存数据库中的键值对来保存数据库状态,而AOF是通过保存服务器的写命令来实现持久化的。

例如:如果执行如下命令

redis>SET msg "hello"

OK

RDB持久化是将msg键值对保存到RDB文件中

AOF持久化是保存SET命令到AOF文件中

AOF文件

被写入AOF文件内的命令都是以Redis的命令请求格式保存的,对于以上命令,打开AOF文件可以看到

*2\r\n$6\r\nSELECT\r\n$1\r\n0\r\n

*3\r\n$3\r\nSET\r\n$3\r\nmsg\r\n$5\r\nhello\r\n

除了指定数据库的命令SELECT是服务器自己添加的外,其他都是客户写的命令。

AOF持久化的实现

  1. 命令追加(append)
  2. 文件写入
  3. 文件同步(sync)

1》命令追加

当使用AOF方式实现持久化时,服务器在执行完一个命令后,会以协议格式把命令追加到服务器状态的aof_buf缓冲区的末尾

strut redisServer{

      //......

      sds aof_buf;     // AOF缓冲区

      //.......

};

如:当服务器执行完命令

redis>SET KEY VALUE

OK

一下内容会追加到缓冲区末尾

*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n

2》AOF文件的写入和同步

Redis服务器进程就是一个事件循环,这个循环包括两种事件来运作,一类是文件事件,执行接收客户端命令和回复命令,另一类是时间事件,像RDB中的 serverCron 函数一样,负责定时运行。

每一个循环事件结束之前,它都会调用 flushAppendOnlyFile 函数,考虑是否需要将 aof_buf 中的内容写入和保存到AOF文件中。

循环执行过程如下:

while true{

        //处理文件事件,接收命令请求以及发送命令回复

        // 处理命令时可能会有新内容被追加到aof_buf缓冲区中

         processFileEvents();

         //处理时间事件

         processTimeEvents();

         //判断是否将aof_buf中的内容写入到AOF文件中

         flushAppendOnlyFile();

}

flushAppendOnlyFile 函数的保存条件由 appendfsync 的值来决定

#always : 将 aof_buf 中的所有内容写入并同步

#everysec :将 aof_buf 所有内容写入AOF文件中,如果上次同步AOF文件的时间到现在超过1秒,将再次对AOF文件同步,并且这个同步操作是有一个线程专门执行。

#no :将 aof_buf 内容写入AOF文件,但不对AOF文件同步,何时同步由操作系统决定

ps:写入和同步

当用户调用write函数进行数据写入时,操作系统会将数据暂时保存到一个内存缓冲区里,当缓存区满了或达到一定时限才存入磁盘,此法虽提高效率,但是存在一定风险,如果计算机停机,缓存区的内容将会消失。为此,系统提供了fsync和fdatasync两个函数,他们可以强制让操作系统立即将缓存区内容写入磁盘,这就是同步。

AOF持久化的效率和安全性
appendfsync的选项效率和安全性
always        每次事件循环都会将aof_buf中的内容写入,虽然效率是三个选项中最慢的,但是也是最安全的,因为即使数据丢失,也是丢失一个事件循环所产生的命令。
everysec        每次时间循环都会进行写入,每隔一秒在子线程中对AOF文件同步一次,效率比always 快,安全性也还不错,因为即使服务器停机了也只丢失一秒内的数据。
no        每个事件循环都进行写入,但是同步由操作系统控制,所以 文件写入命令flushAppendOnlyFile 的调用 无需执行同步操作,所以写入效率是三个选项中最快的。但是安全性比较差,停机会丢失上次同步之后的所有数据。

 

AOF文件的载入与数据还原

Redis读取AOF文件的步骤大致分为四部分

1》创建一个伪客户端,因为AOF存储的是命令,所以只能在客户端上下文中执行,伪客户端执行命令与用户发来的执行命令效果一样。

2》从AOF文件中读取一条命令

3》使用伪客户端执行这条命令

4》重复步骤2、3直至AOF命令处理完毕

 

AOF重写

       重写的原因就是AOF文件存储的命令体积太过庞大,导致服务器运行受阻,为了解决AOF文件体积膨胀问题,Redis采用了重写功能,Redis服务器创建一个新的AOF文件来代替原有的AOF文件,新旧AOF文件保存的数据库状态一样,但是新文件不会包括任何冗余命令。

重写的实现如下:

       虽然此功能叫重写,但是创建新文件并不需要对原有的文件进行任何读取、分析或者写入操作,这个功能是通过读取现在服务器数据库状态实现的。

  例如:

执行命令

RPUSH list "1" "2" " 3" "4"   //  {"1","2","3","4"}

(integer 4)

LPOP list                              //  {"2","3","4"}

“1”

RPUSH list "5"                       //  {"2","3","4","5"}

(integer 4)

在原来的AOF文件中需要保存3条命令   , 而重写的AOF文件只需要查询当前数据库状态  然后保存 

RPUSH list "2" " 3" "4" “5”  这一条命令代替那六条命令

伪代码如下:

def  aof_rewrite(new_aof_file_name){

f= creat_file(new_aof_file_name)                         //创建新的AOF文件

for db in redisServer.db:                                      //遍历数据库

           if db.is_empty():continue                         //  跳过空数据库

           f.write_command("SELECT" + db.id)       // 写入SELECT命令指定数据库id

           for key in db:                                          // 遍历数据库中所i有的键

                     if key.type == String :                    //根据键的类型重写

                               rewrite_string(key)

                     elif key.type == List:

                               rewrite_list(key)

                     elif key.type == Hash :

                               rewrite_hash(key)

                     elif key.type == Set :

                               rewrite_set(key)

                     elif key.type == SortedSet :

                               rewrite_sorted_set(key)

                     if  key.have_expire_time() :                 //  如果键有过期时间,过期时间也会被重写

                               rewrite_expire_time(key)

f.close()                                                                  //写入完毕,关闭文件

}

因为aof_rewrite 函数生成的新文件只会保存必须的命令,所以 新文件不会浪费任何磁盘空间

ps:为了避免客户端缓冲区溢出,当检测到 list、set、hash、sortedset 这四种(单个键)的元素数量超过了redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD(64)常量的时候,重写程序将使用多条命令来记录键的值。

AOF后台重写:因为Redis服务器是单线程处理命令请求的,所以直接调用 aof_rewrite 函数的话,线程就被阻塞了,无法处理客户端发来的其他命令。所以AOF重写程序放在的子进程里执行,所以就达到了一下效果:

  • 子进程进行AOF重写,父进程继续处理请求。
  • 子进程带有服务器的数据副本,之所以使用子进程而不是线程,是因为避免使用锁的情况下,保证数据的安全性

使用子进程还有一个问题需要解决:当子进程进行重写的时候,父进程还会接收客户端发来的命令,导致数据不一致问题。

为了解决这一问题,Redis设置了一个AOF缓冲区和AOF重写缓冲区,AOF缓冲区在子进程创建后开始使用

在子进程进行重写过程中,服务器还要进行如下工作:

  • 1)执行客户端发来的命令
  • 2)将执行的写命令追加到AOF缓冲区
  • 3)将执行的写命令追加到AOF重写缓冲区

从子进程创建以来,服务器执行的所有写命令都会被记录到AOF重写缓冲区里

在子进程执行完重写后,会给父进程发一个信号,父进程收到该信号会执行如下操作以保持数据的一致性:

1》把AOF重写缓冲区的写命令写入到新AOF文件中去

2》对新AOF文件进行改名并原子地覆盖原AOF文件

 

这就是AOF后台重写,即BGREWRITEAOF命令的实现原理。

 

此篇文章是总结Redis设计与实现第11章的内容,更详细的可以去查阅此书。

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值