当前位置 博文首页 > 大漠雁:redis aof细节

    大漠雁:redis aof细节

    作者:[db:作者] 时间:2021-07-30 15:06

    aof流程

    在这里插入图片描述

    Redis把更新命令记录到AOF文件,分为两个阶段:

    阶段1:把更新命令写入aof缓存,如下图示:

    在这里插入图片描述

    阶段2: 把aof缓存写入文件

    在命令添加到aof_buf内后,每次事件循环开始,会调用flushAppendOnlyFile(int force)来将aof_buf写到硬盘上。
    aof.c/flushAppendOnlyFile 函数执行以下两个工作:
    WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件。
    SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中

    系统调用write和fsync说明:

    ?write操作会触发延迟写(delayed write)机制。
    Linux在内核提供页缓冲区用来提高硬盘IO性能。write操作在写入系统缓冲区后直接返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区页空间写满或达到特定时间周期。同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
    ?fsync针对单个文件操作(比如AOF文件),做强制硬盘同步,fsync将阻塞直到写入硬盘完成后返回,保证了数据持久化。

    1. 为什么要调用fsync函数,不是已经调用write把数据写入到文件了吗?
    2. fsyncByPolicy有几种策略?

    首先回答问题1,为什么写入文件后,还要调用fsync函数:
    大多数unix系统为了减少磁盘IO,采用了“延迟写”技术,也就是说当我们执行完write调用后,数据并不一定立马被写入磁盘(可能还是保留在系统的buffer cache或者page cache中),这样当主机突然断电,这些我们本以为已经写入到磁盘文件的数据可能就会丢失;所以当我们需要确保数据被完整正确的写入磁盘(譬如数据库的持久化),则需要调用同步函数fsync,它会一直阻塞直到数据全部被写入到硬盘

    问题2,调用fsync函数的策略,它一共有三种:
    a. AOF_FSYNC_NO :每次都会把aof_buf中的内容写入到磁盘,但是不会调用fsync函数;
    b. AOF_FSYNC_ALWAYS :每次都会把aof_buf中的内容写入到磁盘,同时调用fsync函数;
    c. AOF_FSYNC_EVERYSEC

    由于AOF_FSYNC_ALWAYS每次都写入文件都会调用fsync,所以这种flush策略可以保证数据的完整性,缺点就是性能太差(因为fysnc是个同步调用??,会阻塞主进程对客户端请求的处理),而AOF_FSYNC_NO由于依赖于操作系统自动sync,因此不能保证数据的完整性;

    那有没有一种折中的方式:既能不过分降低系统的性能,又能最大程度上的保证数据的完整性,答案就是:AOF_FSYNC_EVERYSEC,AOF_FSYNC_EVERYSEC的flush策略是:定期(至少1s)去调用fsync,并且该操作是放到一个异步队列中(线程)去执行,因此不会阻塞主进程

    AOF 后台执行的方式和 RDB 有类似的地方,fork 一个子进程,主进程仍进行服务,子进程执行 AOF 持久化,数据被 dump 到磁盘上。
    与 RDB 不同的是,后台子进程持久化过程中,主进程会记录期间的所有数据变更(主进程还在服务),并存储在 server.aof_rewrite_buf_blocks 中;后台子进程结束后,redis 更新缓存追加到 AOF 文件中,是 RDB 持久化所不具备的。(AOF缓冲区和AOF重写缓冲区,AOF_REWRITE也利用了同样的机制)

    每一秒钟保存一次策略:

    1.主线程负责AOF缓冲区
    2.AOF线程负责每秒一次同步磁盘操作,并记录最近一次同步时间.
    3.主线程对比AOF同步时间:
    3.1如果距离上次同步时间在两秒内,主线程直接返回。
    3.2如果距离上次同步时间超过两秒(意思是现在还在同步),主线程将会被阻塞, 直到同步完成。

    在这种模式中,SAVE 原则上每隔一秒钟就会执行一次,因为 SAVE 操作是由后台子线程调用的,所以它不会引起服务器主进程阻塞
    注意,在上一句的说明里面使用了词语“原则上” ,在实际运行中,程序在这种模式下对 fsync或 fdatasync 的调用并不是每秒一次,它和调用 flushAppendOnlyFile 函数时 Redis 所处的状态有关
    每当 flushAppendOnlyFile 函数被调用时,可能会出现以下四种情况:
    ? 子线程正在执行 SAVE ,并且:
    1. 这个 SAVE 的执行时间未超过 2 秒,那么程序直接返回,并不执行 WRITE 或新的SAVE 。
    2. 这个 SAVE 已经执行超过 2 秒,那么程序执行 WRITE ,但不执行新的 SAVE。 注意,因为这时 WRITE 的写入必须等待子线程先完成(旧的)SAVE ,因此这里WRITE 会比平时阻塞更长时间
    ? 子线程没有在执行 SAVE ,并且:
    3. 上次成功执行 SAVE 距今不超过 1 秒,那么程序执行 WRITE ,但不执行 SAVE
    4. 上次成功执行 SAVE 距今已经超过 1 秒,那么程序执行 WRITE 和 SAVE(主线程write,后台线程fsync的并发行为)
    在这里插入图片描述
    根据以上说明可以知道,在“每一秒钟保存一次”模式下,如果在情况 1 中发生故障停机,那么用户最多损失小于 2 秒内所产生的所有数据。
    如果在情况 2 中发生故障停机,那么用户损失的数据是可以超过 2 秒的
    Redis 官网上所说的,AOF 在“每一秒钟保存一次”时发生故障,只丢失 1 秒钟数据的说法,实际上并不准确。

    推荐翻译redis官方原文:Redis 响应延迟问题排查
    参考:
    Redis AOF持久化
    Redis系列之(三)——持久化与复制
    redis的aof持久化深入解析

    cs
    下一篇:没有了