
一、背景关于落盘你需要了解的为什么需要关心落盘因为一个数据从客户端发送到进入KV存储的持久化介质需要进过以下步骤客户端向服务端发送写操作数据库服务端接收到写请求的数据数据在服务端的内存中服务端调用write系统调用将数据往磁盘上写数据在系统Page cache中操作系统将缓冲区中的数据转移到磁盘控制器上数据在磁盘缓存中磁盘控制器将数据写到磁盘的物理介质中数据真正落到磁盘上而我们都知道数据到了内存中仍然是断电易失的然而其实write之后也只是从堆内存走到了内核内存实际的存储介质仍然是易失的DRAM第四步到了磁盘缓存仍然只走到了磁盘控制器的DRAM所以真正语义上的持久化只有第5步。保证数据走到第5步的功能就叫落盘刷盘。落盘策略盘点以 Redis 为例最典型的落盘策略有no不主动刷盘always每条数据都必须刷盘everysec每秒刷一次盘从数据安全的角度说这三种策略的危险窗口如下策略机制no可靠性依赖内核的异步回写机制系统崩溃将丢失未刷新的脏页数据。always每次写入均同步落盘提供最强的持久性保证但代价是退化为同步 I/O 模型的性能瓶颈。everysec通过每秒同步一次来缩小数据丢失窗口仅在系统故障时丢失最近一秒内的写入。博客内容比较 everysec 配置下后台线程落盘策略 vs io_uring 性能vemory 对比 Redis AOF 性能io_uring 异步落盘架构图与分析二、everysec 策略下的对比后台线程落盘everysec 的落盘策略下Redis 采用的是后台线程的方案我也实现了类似的架构作为 io_uring 不可用时的方案。项目AOFECHOSETGETKV存储关闭13650.5713725.7113556.56KV存储开启13518.2613276.3313229.96Redis关闭12373.0211768.5812042.97Redis开启11993.1410091.6312193.19io_uring 异步落盘项目AOFECHOSETGETKV存储关闭13865.5913673.3413611.00KV存储开启13667.3613211.0913278.45从 AOF 缓冲区取数据可以是一条也可以是一块取决于实现策略然后构建写sqe调io_uring_get_sqe()-io_uring_prep_write()-io_uring_submit()用于write IO请求外部定时器回调每秒钟构建一次刷盘sqe调io_uring_prep_fsync()刷盘sqe的标志位配置为IOSQE_IO_DRAIN含义是等先前提交的全部sqe全部完成才开始刷盘。主线程的事件循环中插入io_uring_peek_cqe()非阻塞检查完成事件io_uring_cqe_seen()收割cqe开启 iouring AOF 后SET下降不到4%GET不到3%但是没有和后台线程方式拉开差距。分析这里展示的是基本的 io_uring AOF 异步刷盘架构可以确定节约的开销是少一条常驻的后台线程。实际上一条条地取出指令并逐条prep_write并没有完全发挥 io_uring 真正的优势。我以后会写博客专门谈论这方面的优化。