Benjamin

静以修身,俭以养德,非澹薄无以明志,非宁静无以致远。
随笔 - 397, 文章 - 0, 评论 - 196, 引用 - 0
数据加载中……

Reids持久化之AOF

一、AOF(Append Only File):持久性记录服务器接收到的每个写操作。然后可以在服务器启动时再次重播这些操作,从而重建原始数据集。命令使用与 Redis 协议本身相同的格式进行记录;注意:AOF文件只会记录Redis的写操作命令,因为读命令对数据的恢复没有任何意义
1、AOF文件说明:*2表示当前命令有2个部分,每部分都是由$+数字开头,后面紧跟着具体的命令、键或值。数字表示这部分中的命令、键或值一共有多少字节。例如,$6 SELECT表示这部分有 6 个字节,也就是SELECT命令。

2、AOF日志的生成过程:Redis成功执行写操作指令,然后将写的指令按照自定义格式追加到aof_buf缓冲区,这是第一个缓冲区Redis主进程将aof_buf缓冲区的数据写入到内核缓冲区,这是第二个缓冲区;根据AOF同步策略适时地将内核缓冲区的数据同步到磁盘,过程结束。
命令传播:Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF程序中。
     AOF日志写入是在Redis成功执行命令之后才进行的,可避免记录错误指令情况,同时不会阻塞当前写操作。这样做的风险,如果在写AOF日志时宕机,会导致指令和相关参数丢失,这种情况一般是磁盘的时机有关,写磁盘的频率越高,发生数据丢失的可能性就越小;另一个风险就是有可能阻塞下一个操作,这个和写文件方式和时机有关,如果Redis每次成功执行指令之后都力图将当前指令同步到AOF文件,开销必然很大。
      因此Redis引入了缓冲区的概念,缓冲区对应了文件的写入方式(不求一步到位,允许循序渐进地写入),而何时将缓冲区的内容彻底同步到文件就涉及到了AOF的同步策略(写回磁盘的时机)
②命令追加:在AOF开启的情况下,Redis会将成功执行的写指令以上文我们讲过的协议格式追加到Redis的aof_buf缓冲区。aof_buf 缓冲区保存着所有等待写入到AOF 文件的协议文本。
③文件写入:
Redis的主服务进程本质上是一个死循环,循环中有负责接受客户端的请求,并向客户端发送回执的逻辑。在AOF功能开启的情况下,文件事件会将成功执行之后的写命令追加aof_buf缓冲区,在主服务进程死循环的最后,会调用flushAppendOnlyFile函数,该函数会将aof_buf中的数据写入到内核缓冲区,然后判断是否应该进行同步。而是否进行同步则是由Redis配置中的appendOnlyFile选项来决定的
      用户调用write函数将数据写入到文件时,操作系统内核会将数据首先保存在内存缓冲区中,等到缓冲区的空间被填满或者到达一定的时机之后,内核会将数据同步到磁盘。这种同步过于依赖于操作系统内核,操作系统提供了fsyncfdatasync两个同步函数,可以强制内核立即将缓冲区内的数据同步到磁盘。伪代码
void eventLoop {
    
    while(true){
        
        // ...

        // 文件事件,接受命令请求,返回客户端回执
        // 根据aof功能是否开启,决定是否将写命令追加到aof_buf缓冲区
        handleFileEvents();

        // 将aof_buf数据写入内核缓冲区
        // 判断是否需要将数据同步到磁盘
        flushAppendOnlyFile();
        
        // ...

    }
   
};
④文件同步:redis.conf配置文件中appendOnlyFile的选项有三个值可选,对应三种AOF同步策略。
1)No同步时机由内核决定; 写命令执行完先把日志写入AOF文件的内核缓冲区,待系统缓存已满或定期保存机制触发或Redis或AOF关闭       才同步到磁盘,这个同步是阻塞的。写入操作也是阻塞的。这个阶段宕机由系统内核及运行环境确定,不确定性较大。
2)Everysec每一秒钟同步一次。也是写道内核缓存区,间隔1秒同步到磁盘,有子线程执行不会阻塞。
 实际运行中该模式对fsyncfdatasync的调用并不是每秒一次,而是和调用flushAppendOnlyFile函数时Redis所处的状态有关。
  flushAppendOnlyFile 函数被调用时, 可能会出现以下四种情况:
   子线程正在执行同步,同步的执行时间未超过 2 秒,那么程序直接返回,如宕机,损失数据在2秒内的数据。
   子线程正在执行同步,同步已经执行超过 2 秒(超时),程序执行写入操作 ,但不执行新的同步操作 。这时的写入操作必须等待子线程先完成原本的同步操作 ,因此这里的写入操作会比平时阻塞更长时间,此时宕机,损失数据超过两秒。
 子线程没有在执行同步,上次成功执行同步距今不超过1秒,那么程序执行写入,但不执行同步 
 子线程没有在执行同步,上次成功执行同步距今已经超过1秒,那么程序执行写入和同步 

3)Always每执行一个命令同步一次。同步操作是由 Redis 主进程执行的,所以在同步执行期间,主进程会被阻塞,不能接受命令请求。

二、AOF文件的载入和数据还原:AOF文件中包含了能够重建数据库的所有写命令,因此将所有命令读入并依次执行即可还原Redis之前的数据状态。
1、创建一个不带网络连接的伪客户端(fake client),伪客户端执行命令的效果, 和带网络连接的客户端执行命令的效果完全相同;
2、
读取AOF所保存的文本,并根据内容还原出命令、命令的参数以及命令的个数;
3、根据指令、指令的参数等信息,使用伪客户端执行命令。
4、
执行 2 和 3 ,直到AOF文件中的所有命令执行完毕。

三、AOF重写:AOF的作用是帮我们还原Redis的数据状态,其中包含了所有的写操作,但是正常情况下客户端会对同一个KEY进行多次不同的写操作;这样被频繁操作的键有很多的话,AOF文件的体积就会急速膨胀。
  AOF文件的体积受操作系统大小的限制,本身就不能无限增长;体积过于庞大的AOF文件会影响指令的写入速度,阻塞时间延长;AOF文件的体积越大,Redis数据恢复所需的时间也就越长。
  Redis提供了rewriteAOF重写功能来精简AOF文件体积
   
  
AOF重写的实现原理:AOF文件的生成是读取Redis当前的数据状态来重新生成的.重写过程是由子进程执行bgrewriteaof来完成的。这样处理的最大好处是:AOF重写期间,不影响主进程处理命令请求;子进程带有主进程的数据副本,操作效率更高.
  如果使用线程,在处理共享内存的时候,必须使用同步对象,这样会影响其性能。
  使用子进程,会使用写时拷贝,fork子进程的时候,子进程会拷贝父进程的页表,即虚实映射关系,不是物理内存;父子进程一方写操作,触发写时拷贝机制,于是父子进程就有了独立的数据副本,就不用加锁来保证数据安全。
     
fork子进程的过程中,父进程的页表越大阻塞的时间也越长,不过通常而言该过程是非常快。
   fork完子进程后,如果父子进程任意一方修改了共享数据,就会发生**「写时复制」**,这期间会拷贝物理内存,如果内存越大,自然阻塞的时间也越长;这里复制粒度是一个内存页,如果只是修改一个256B的数据,父进程需要读原来的整个内存页,然后再映射到新的物理地址写入。一读一写会造成读写放大。如果内存页越大(例如2MB的大页),那么读写放大也就越严重,对Redis性能造成影响。因此使用Redis的AOF功能时,需要注意页表的大小不要设置的太大。
  子进程在进行 AOF 重写期间主进程还需要继续处理命令,而新的命令可能对现有的数据进行修改, 会让当前数据库的数据和重写后的 AOF 文件中的数据不一致,此时Redis引入了另一个缓冲区的概念——AOF重写缓冲区

因此当子进程在执行AOF重写(bgrewriteaof)时, 主进程需要执行以下三个工作:处理客户端的命令请求;将写命令追加到AOF缓冲区aof_buf);将写命令追加到AOF重写缓冲区

当子进程完成 AOF重写之后, 它会向父进程发送一个完成信号, 父进程在接到完成信号之后, 会调用一个信号处理函数, 并完成以下工作:
将 AOF重写缓冲区中的内容全部写入到新AOF 文件中;完毕之后, 现有 AOF 文件、新 AOF 文件和数据库三者的状态就完全一致了
对新的 AOF 文件进行改名,覆盖原有的 AOF 文件。注意,这是一个原子操作,改名过程中不接受客户端指令。完毕之后, 程序就完成了新旧两个 AOF 文件的交替。主进程就可以继续像往常一样接受命令请求了。 在整个 AOF 后台重写过程中, 只有将AOF重写缓冲区数据写入新AOF文件和改名操作会造成主进程阻塞, 其他时候, AOF 后台重写都不会对主进程造成阻塞, 这将 AOF 重写对性能造成的影响降到了最低。

五、AOF后台重写触发条件
1、AOF的其他两个配置
   auto-aof-rewrite-percentage 100
   auto-aof-rewrite-min-size 64mb
2、
AOF 重写可以由用户通过调用 bgrewriteaof手动触发。
3、服务器在 AOF 功能开启的情况下, 会维持以下三个变量:
记录当前 AOF 文件大小的变量 aof_current_size;
记录最后一次 AOF 重写之后, AOF 文件大小的变量 aof_rewrite_base_size;
增长百分比变量 aof_rewrite_perc
4、当Redis中的定时函数 serverCron 执行时, 它都会检查以下条件是否全部满足, 如果是的话, 就会触发自动的 AOF 重写:没有 bgsave 命令在进行。没有 bgrewriteaof 在进行。当前 AOF 文件大小大于 我们设置的auto-aof-rewrite-min-size。当前 AOF 文件大小和最后一次 AOF 重写后的大小之间的比率大于等于指定的增长百分比auto-aof-rewrite-percentage
默认情况下, 增长百分比为 100% , 也即是说, 如果前面三个条件都已经满足, 并且当前 AOF 文件大小比最后一次 AOF 重写时的大小要大一倍的话, 那么触发自动 AOF 重写。

六、总结:
      AOF是将Redis的所有写日志同步到磁盘的一种持久化方法,通过执行AOF中记录的所有指令可以达到恢复Redis原始数据状态的目的。
      对于指令的同步时机,Redis提供了三种AOF同步策略,分别是NoEverysecAlways,三种策略对Redis性能的负面影响是由低到高的,在数据可靠性上也是由低到高的。
      为了解决AOF日志太大的问题,Redis提供了AOF重写的机制,利用「写时复制」和「AOF重写缓冲区」达到精简AOF文件的目的。


posted on 2024-07-15 15:06 Benjamin 阅读(4) 评论(0)  编辑 收藏 引用 所属分类: 数据库


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理