AOF落盘几点小细节

AOF运行时间:
在beforeSleep()中,即

//事件处理器的主循环
void aeMain(aeEventLoop *eventLoop) {

    eventLoop->stop = 0;

    while (!eventLoop->stop) {

        // 如果有需要在事件处理前执行的函数,那么运行它
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);

        // 开始处理事件
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
    }
}

我们知道AOF是用来把客户端的写同步到硬盘上的,客户端的写发生在eventLoop中(即文件事件驱动)。
因此是下一次循环的事件触发前,对上一次的数据做beforeSleep处理,即AOF处理。所以这里自然需要一个缓冲区来操作。

将数据刷到硬盘则需要通过系统调用fsync(阻塞操作)。由于IO速度比CPU处理速度慢太多,如果Redis每执行一次写命令,我们就通过系统调用write和fsync往硬盘中相应的AOF文件写一条记录,由于其为阻塞I/O的特性,导致主进程的主线程挂起,在该段时间内无法服务新的请求,因而吞吐量受到影响。

fsync有三种策略:
1.Always:每次事件循环都进行一次同步操作,主线程阻塞着做。极值完整度。
2.Everysec:每秒进行一次同步操作,开一个专门操作IO的线程。丢失一秒数据,完整度和性能的平衡。
3.No:由操作系统控制同步操作,操作系统选取落盘时间,不阻塞主线程。极值性能。

通过阅读源码,我们可以发现以下几点结论:
1.在每秒(EverySec)模式下,AOF不一定立刻能把缓冲区的内容IO完。所以可能下一次主线程跑到beforeSleep时,上一次的IO还没做完。所以这是fsync的运行标志位(sync_in_progress)就还是1。这样AOF会有一个推迟2秒的操作,如果推迟了2秒还没做完,主线程就会阻塞着帮忙做了write。
2.强制执行(配置force值),也是主线程阻塞着帮忙write。
3.这里就产生了多线程问题,为了安全性这边用的是原子操作。
4.关于AOF缓存的内存空间大小。如果这个缓存区足够小(小于4K),缓存就会留着被重用。否则释放这个AOF缓存。

关于重写:
有时候反复对一条数据修改,已落盘的AOF会太大。
所以就有了AOF重写机制。它其实是根据现有数据库的情况,直接重新生成一个AOF重写日志,只将当前有效并存在的数据转义成符合AOF日志格式的记录,接着依次写入该日志文件,最后通过fsync系统调用刷入硬盘。这些操作都是在子进程中进行的。
子进程还带来了一个问题,就是在子进程在生成AOF重写日志的时候,主进程还在处理新的请求,那么这段区间的写命令是没有记录下来的。如下图所示,这里AOF重写缓冲区可以接收重写期间的服务器操作,然后主线程调用write,阻塞得去做追加这件事情。出自于源码函数backgroudRewriteDoneHandle。
在这里插入图片描述

Huge Page问题
虽然AOF重写日志的生成是在子进程中进行的,一般不会阻塞主进程,但还是有一定几率会发生阻塞的,这就是fork的原因。

fork理论上该子进程会有父进程内存空间的完整拷贝,但Linux对fork做了优化,引入了Copy on Write(写时复制,读时共享)机制,也就是只有当主进程修改了内存页的时候,操作系统会在此操作前拷贝一个副本,将其复制到另一个内存页中,供子进程使用。

那么当内存页设置的过大时,在进行副本拷贝时,申请内存页时,可能需要等待内存页被释放,失败的概率会大一些,为了保证子进程能获得副本,应而导致主进程发生阻塞,无法处理新来的请求,导致吞吐量下降。

可以通过对/sys/kernel/mm/transparent_hugepage/enabled进行设置来关闭这个功能。

主从复制也有可能产生这个问题。同样也是fork的原因。当某个主服务器的redis实例太大了,在fork的时候需要拷贝一套内存页表给子进程,当实例内存过大,页表大小也会过大,这个拷贝页表的动作是主线程阻塞着做的。

总结:
AOF重写和主从复制其实本质上都是把当前的redis内存实例数据落盘。由于涉及IO都需要开子进程来做,以避免主线程阻塞。
开子进程用fork(),由于有写时复制读时共享的机制(在这种机制下,fork的唯一开销就是页表复制),所以不需要真的完全复制一份父进程资源给子进程,但是至少需要一份子进程的内存页表。然后子进程和父进程的虚拟内存就都有了,一开始映射到同样的物理内存上,然后通过写时复制再去给子进程申请新的物理内存。

所以规避法则是:
1.要么干脆不做AOF重写
2.要么保证单个redis实例不是太大

### Redis AOF 持久化配置与使用 #### 什么是AOF持久化? AOF(Append Only File)是Redis的一种持久化机制,它通过记录服务器接收到的每一个写操作命令来实现数据的持久性。当Redis重启时,可以通过重新执行这些命令来重建整个数据集的状态[^3]。 #### AOF持久化的启用方法 要启用AOF持久化功能,在Redis配置文件`redis.conf`中设置以下参数: ```conf appendonly yes ``` 保存配置文件并退出编辑器后,需重启Redis服务以使更改生效[^1]。 #### AOF持久化的基础流程 AOF持久化的核心在于将每次写入操作追加到日志文件中。以下是其工作原理的关键点: - **写入过程**:每当有新的写命令到达时,Redis将其附加到内存缓冲区。 - **同步策略**:通过`appendfsync`选项控制如何以及何时将缓冲区的内容刷新到硬上。可选值包括 `always`, `everysec`, 和 `no`[^2]。 - `always`: 每次写入都会立即刷,性能最低但最安全; - `everysec`: 默认推荐模式,每秒刷一次,兼顾性能和安全性; - `no`: 完全依赖操作系统决定何时刷,可能丢失较多数据。 #### 配置示例 下面是一个典型的AOF相关配置片段: ```conf # 启用AOF持久化 appendonly yes # 设置AOF文件路径 (默认为 appendonly.aof) appendfilename "appendonly.aof" # 控制AOF文件同步频率 appendfsync everysec # 自动重写条件 auto-aof-rewrite-min-size 64mb auto-aof-rewrite-percentage 100 ``` 上述配置表示开启AOF持久化,并指定每秒钟同步一次数据至磁;同时定义了触发AOF重写的最小尺寸及增长比例阈值[^4]。 #### AOF的优点与适用场景 相比另一种常见的RDB持久化方式,AOF具有更高的可靠性,因为它逐条记录所有的修改指令,即使发生意外宕机也能最大程度减少数据损失。然而,这也使得AOF文件通常较大,因此适合对数据完整性要求较高的应用环境。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值