Redis AOF 持久化 实现原理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis AOF 持久化 实现原理相关的知识,希望对你有一定的参考价值。

参考技术A From now on, every time Redis receives a command that changes the dataset (e.g. SET)

it will append it to the AOF.

When you restart Redis it will re-play the AOF to rebuild the state.

写入的命令以追加到文件的形式保存到文件中 重启的时候对命令进行重现

AOF 是解决 RDB 会丢失一部分数据则出来的持久久,每写一条命令,就往文件中追加一次

但这样有可能会就会造成一个文件非常的大,并且在重启的时候,对命令重现的时候,非常的久

所以 AOF 也有类似的镜像模式

3.1 AOF 状态

3.2 重要变量

假如 一直往一个文件中追加命令,那想想,那一个文件不是有可能无限大了?

再想想,假如一个key写一1000万次,那这key就得重现1000万次,那感觉是不是不爽?

所以作者也实现了 AOF rewrite,在一定情况下,将内存中的数据镜像的形式刷到文件,然后在那个时间点之后再进行 命令追加的形式保存

4.1 AOF rewrite 开启一个子进程

和 RDB 开启一个子进程一样,以 copy on write 的技术开启一个子进程

4.2 AOF rewrite 刷盘过程

4.2.1 创建一个 temp-rewriteaof-%d.aof 文件,并打开

4.2.2 将 aof rewrite 镜像数据以二进制形式追加到文件 temp-rewriteaof-%d.aof(这个过程是到内核)

4.2.3 每 32MB 数据将数据从内核fsync方式刷到磁盘

4.2.4 关闭句柄

4.2.5 将 temp-rewriteaof-%d.aof 改名为 temp-rewriteaof-bg-$pid.aof

4.3 AOF rewrite 子进程结束之后回调

在 aof rewrite 子进程结束之后,由主进程回调函数,根据 appendfsync 配置进行一次刷盘调用

1). 在rewrite期间新增的数据 追加到 temp-rewriteaof-bg-$pid.aof

2). 将AOF文件名修改为 appendonly.aof

3). aways 刷盘模式而主线程立马调aof_fsync 刷盘

4). everysec 刷盘模式而提交给另外一个线程异步刷盘

5). 提交给关闭句柄的线程,关闭 旧的AOF文件句柄

4.4 AOF rewrite 触发

4.4.1 定时触发

1). 默认100ms 检测一次

2). 没有rdb,aof rewrite 正在执行

3). 当前AOF数据大小 大于 设置的最小rewrite大小 ,默认64MB(auto-aof-rewrite-min-size参数)

4). (server.aof_current_size*100/base) - 100 >= server.aof_rewrite_perc 当前AOF数据大小 是否是上一次的多少倍,默认为 1 倍 (auto-aof-rewrite-percentage 参数)

4.4.2 手工触发 - BGREWRITEAOF 命令

立马开启一个子进程进行rewrite

但命令结果返回给客户端之后不一定rewrite完了

4.4.3 手工触发 - CONFIG appendonly yes

当原本是关闭的情况下

立马开一个子进程rewrite

假如rewrite失败了,每次定时任务都会尝试重试rewrite

每条写命令在更新了内存之后,都会有一个buf 来保存每命令的数据,不管 appendfsync 配置的是什么参数,这个buf 都是 Redis 自已的,并不是刷到内核里

1) . 只要开启了 AOF,就写到 server.aof_buf

2). 假如开启了rewrite进程 期间,将数据额外追加一份到 server.aof_rewrite_buf_blocks。这份数据是用于在 AOF rewrite 进程退出之后,立马将这份数据刷到 aof 文件末尾的

 /* AOF postponed flush: Try at every cron cycle if the slow fsync

    * completed. */

    if (server.aof_flush_postponed_start) flushAppendOnlyFile(0);

    /* AOF write errors: in this case we have a buffer to flush as well and

    * clear the AOF error in case of success to make the DB writable again,

    * however to try every second is enough in case of 'hz' is set to

    * an higher frequency. */

    run_with_period(1000)

        if (server.aof_last_write_status == REDIS_ERR)

            flushAppendOnlyFile(0);

   

1). 每秒触发一次调用刷盘函数

2). 每次调用函数刷盘,会判断上一次刷盘是否完成,

如果没有完成,则100ms后的定时任务继续调用

3). 将数据从buf刷到内核

4). 提交给另外一个线程 异步调用aof_fsync 将数据从内核刷到磁盘

每条命令都刷盘一次

1). 每个事件处理之前 将server.aof_buf 里的数据刷到内核。记住并不是每条命令之后立马将数据落盘,而是等待下一个事件执行之前落盘

2). 主线程调用 aof_fsync 从内核刷到磁盘。这里是由主线程执行调用 fsync 刷到磁盘

1). 每个事件处理之前 将server.aof_buf 里的数据刷到内核

2). 将 buf 刷到内核之后,将由内核决定什么时候刷到磁盘,redis 进程不再接管flush这个事情

1). AOF 也会有rewrite刷盘,也是开子进程,cony on write

2). AOF 默认情况下是 当前AOF数据大小是上一次rewrite的倍的时候触发

3). AOF everysec 刷盘,是每秒从buf刷到内核后fsync刷到磁盘, 并不是每条命令就往内核里刷一次数据

4). AOF aways 刷盘,是写命令后的下一个事件执行之前由主线程执行刷盘的,并非写命令之后立马刷盘

缓存数据库REDIS之二:AOF重写原理

目录

一、为什么使用AOF

二、AOF 重写作用

三、AOF文件重写后为什么会缩减文件大小

四、AOF 文件重写的实现

4.1.AOF执行流程

4.2.实现原理

 4.3.AOF缓冲区和缓存区的作用及效用

4.4.AOF新旧替换时,主进程状态

4.5.AOF的三个策略配置

4.6.AOF文件重写的触发条件及触发方式

4.6.1触发条件

4.6.2触发方式

五、总结


一、为什么使用AOF

        RDB持久化是将进程数据写入文件,而AOF持久化,则是将Redis执行的每次写、删除命令记录到单独的日志文件中,查询操作不会记录; 当Redis重启时优先执行AOF文件中的命令来恢复数据。
       与RDB相比,AOF的实时性更好,因此已成为主流的持久化方案。

二、AOF 重写作用

         AOF 持久化是通过保存被执行的写命令来记录数据库状态和相关数据,所以AOF文件的大小随着时间推进会越来越大;那么通过AOF文件进行还原出数据库的时间也会相应增加。 为了解决AOF文件体积膨胀的问题,Redis提供了AOF重写功能:Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件,新旧两个文件所保存的数据库状态是相同的,但是新的AOF文件不会包含任何浪费空间的冗余命令,通常体积会较旧AOF文件小很多。

三、AOF文件重写后为什么会缩减文件大小

  • 过期的数据不再写入文件
  • 无效的命令不再写入文件:如有些数据被重复设值(set mykey v1, set mykey v2)、有些数据被删除了(sadd myset v1, del myset) 等。
  • 多条命令可以合并为一个:如sadd myset v1, sadd myset v2, sadd myset v3可以合并为sadd myset v1 v2 v3。


四、AOF 文件重写的实现

        AOF重写并不需要对原有AOF文件进行任何的读取,写入,分析等操作,这个功能是通过读取服务器当前的数据库状态来实现的。


4.1.AOF执行流程

  • 命令追加(append): 将Redis的写命令追加到缓冲区aof_buf;
  • 文件写入(write)和文件同步(sync):根据不同的同步策略将aof_buf中的内容同步到硬盘;
  • 文件重写(rewrite): 定期重写AOF文件,达到压缩的目的。

(1) 命令追加 (append)
        Redis先将写命令追加到缓冲区,而不是直接写入文件,主要是为了避免每次有写命令都直接写入硬盘,导致硬盘IO成为Redis负载的瓶颈。
        命令追加的格式是Redis命令请求的协议格式,它是一种纯文本格式,具有兼容性好、可读性强、容易处理、操作简单避免二次开销等优点。

       在A0F文件中,除了用于指定数据库的select命令 (如select0为选中0号数据库) 是由Redis添加的,其他都是客户端发送来的写命令。

(2)文件写入(write) 和文件同步 (sync)
        Redis 提供了多种AOF缓存区的同步文件策略,策略涉及到操作系统的write函数和fsync函数,说明如下:
          为了提高文件写入效率,在现代操作系统中,当用户调用write函数将数据写入文件时,操作系统通常会将数据暂存到一个内存缓冲区里,当缓冲区被填满或超过了指定时限后,才真正将缓冲区的数据写入到硬盘里。这样的操作虽然提高了效率,但也带来了安全问题:如果计算机停机,内存缓冲区中的数据会丢失;因此系统同时提供了fsync、fdatasync等同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性。

4.2.实现原理

        首先创建新的AOF文件是通过aof_rewrite函数来实现,但是这个函数会进行大量的写入操作,所以调用这个函数的线程将被长时间的阻塞,因为Redis服务器使用单线程来处理命令请求;所以如果直接是服务器进程调用AOF_REWRITE函数的话,那么重写AOF期间,服务器将无法处理客户端发送来的命令请求。(说白了,就是占用主线程,导致其他请求无法被处理)
        为了解决以上问题,那么Redis将AOF重写程序放到子进程(后台)里执行,这样子进程进行AOF重写期间,主进程可以继续处理命令请求;子进程带有主进程的数据副本,使用子进程而不是线程,可以避免在锁的情况下,保证数据的安全性。
       那么问题又来了,使用子进程进行AOF重写的问题,子进程在进行AOF重写期间,服务器进程还要继续处理命令请求,而新的命令可能对现有的数据进行修改,这会让当前数据库的数据和重写后的AOF文件中的数据不一致。

       为了解决这种数据不一致的问题,Redis增加了一个AOF重写缓存,这个缓存在fork出子进程之后开始启用,Redis服务器主进程在执行完写命令之后,会同时将这个写命令追加到AOF缓冲区和AOF重写缓冲区 。

 4.3.AOF缓冲区和缓存区的作用及效用

        AOF缓冲区的内容会定期被写入和同步到AOF文件中,对现有的AOF文件的处理工作会正常进行从创建子进程开始,服务器执行的所有写操作都会被记录到AOF重写缓冲区中;
        完成AOF重写之后当子进程完成对AOF文件重写之后,它会向父进程发送一个完成信号,父进程接到该完成信号之后,会调用一个信号处理函数,该函数完成以下工作:

        将AOF重写缓存中的内容全部写入到新的AOF文件中;这个时候新的AOF文件所保存的数据库状态和服务器当前的数据库状态一致;

4.4.AOF新旧替换时,主进程状态


       对新的AOF文件进行改名,原子的覆盖原有的AOF文件;完成新旧两个AOF文件的替换。
当这个信号处理函数执行完毕之后,主进程就可以继续像往常一样接收命令请求了。在整个AOF后台重写过程中,
只有最后的“主进程写入命令到AOF缓存”和“对新的AOF文件进行改名,覆盖原有的AOF文件。”这两个步骤(信号处理函数执行期间)会造成主进程阻塞,在其他时候,AOF后台重写都不会对主进程造成阻塞,这将AOF重写对性能造成的影响降到最低。

4.5.AOF的三个策略配置

vim /etc/redis/6379.conf
---729---
● appendfsync always:
命令写入aof_ buf后立即调用系统fsync操作同步到AOF文件,fsync完成后线程返回。这种情况下,每次有写命令都要同步到AOF文件,硬盘IO成为性能瓶颈,Redis只能支持大约几百TPS写入,严重降低了Redis的性能;即便是使用固态硬盘(SSD),每秒大约也只能处理几万个命令,而且会大大降低SSD的寿命。

● appendfsync no:
命令写入aof_ buf后调用系统write操作,不对AOF文件做fsync同步;同步由操作系统负责,通常同步周期为30秒。这种情况下,文件同步的时间不可控,且缓冲区中堆积的数据会很多,数据安全性无法保证。

● appendfsync everysec:
命令写入aof_ buf后调用系统write操作,write完成后线程返回; fsync同步文件操作由专门的线程每秒调用一次。everysec是前述两种策略的折中,是性能和数据安全性的平衡,因此是Redis的默认配置,也是我们推荐的配置。

4.6.AOF文件重写的触发条件及触发方式

4.6.1触发条件

  •         没有BGSAVE命令(RDB持久化)/AOF持久化在执行;
     
  •          没有BGREWRITEAOF在进行;
  •          当前AOF文件大小要大于server.aof_rewrite_min_size(默认为1MB),或者在redis.conf配置了auto-aof-rewrite-min-size大小;
  •          当前AOF文件大小和最后一次重写后的大小之间的比率等于或者等于指定的增长百分比(在配置文件设置了auto-aof-rewrite-percentage参数,不设置默认为100%)
  •         如果前面三个条件都满足,并且当前AOF文件大小比最后一次AOF重写时的大小要大于指定的百分比,那么触发自动AOF重写。

4.6.2触发方式

  •        手动触发:直接调用bgrewriteaof命令,该命令的执行与bgsave有些类似:都是fork子进程进行具体的工作,且都只有在fork时阻塞。
  •        自动触发:通过设置auto-aof - rewrite-min-size选项和auto- aof - rewrite- percentage选项来自动执行BGREWRITEAOF。
  •        只有当auto-aof- rewrite- -min-size和auto-aof -rewrite-percentage两个选项同时满足时,才会自动触发AOF重写,即bgrewriteaof操作。

五、总结

  •         AOF重写的目的是为了解决AOF文件体积膨胀的问题,使用更小的体积来保存数据库状态,整个重写过程基本上不影响Redis主进程处理命令请求;

  •        AOF重写其实是一个有歧义的名字,实际上重写工作是针对数据库的当前状态来进行的,重写过程中不会读写、也不适用原来的AOF文件;

  •        AOF可以由用户手动触发,也可以由服务器自动触发。

以上是关于Redis AOF 持久化 实现原理的主要内容,如果未能解决你的问题,请参考以下文章

Redis---- Redis的持久化机制RDB和AOF原理

Redis---- Redis的持久化机制RDB和AOF原理

Redis---- Redis的持久化机制RDB和AOF原理

缓存数据库REDIS之二:AOF重写原理

缓存数据库REDIS之二:AOF重写原理

缓存数据库REDIS之二:AOF重写原理