初学AOF和RDB很容易让人想到redolog和binlog。都是一个记"物理量",一个记操作。不过RDB直接是二进制文件,而redolog记物理偏移量。
mySQL分了服务层,为了和引擎层解耦。也就出现了服务层的binlog和引擎层的innoDB的redolog 之间的同步问题。而Redis架构是更统一的,没有抽离出服务层,也就没有AOF和RDB的同步问题,并且存在AOF+RDB的混合持久化方案。
概述
AOF记得是操作日志,RDB是数据内容全量刷盘的快照。
AOF
总体流程为:写操作,写入缓冲区server.aof_buf,I/O系统调度,写page cache,写磁盘。
众所周知写磁盘开销很大,有三种写入磁盘时机:Always,主线程直接写入磁盘;Everysec,主线程不阻塞,系统每秒写入;No,由操作系统决定。
AOF重写机制
AOF本身就是上面的内容,特别简单,但是存在优化空间。写操作必然存在很多重复修改,但是我们只需要有最后修改的信息就行了,于是就有了AOF重写机制,也就是精简化AOF,一般在AOF文件满了时触发。
为了保证重写文件不因为中断而污染源文件,不采用原地重写,采用重写新文件,完成覆盖的做法。
重写采用子进程bgrewriteaof来完成:
- 先将物理内存权限设为只读,并为子进程fork一个页表,获得一个固定的虚拟和物理映射表。
- 当写入时,只读报错,触发copy on write(COW)。此时触发复制真实物理内存,在复制内存上进行修改(复制的是仅修改的物理内存,不是全量复制!)。
在复制过程中父进程需要继续刷盘修改内存,必然触发cow,除了原本的AOF缓存区,为了和重写文件必须也再重写时维护一个AOF重写缓存池。当重写完成时,子线程关闭并发给主线程信号,主线程负责将"重写期间存在AOF重写缓存池的AOF日志"追加至AOF重写文件。最后进行覆盖。
RDB
快照机制就简单多了,save方法主线程调用一般不用,bgsave启用子线程。
RDB一样为了在写快照的时候不影响主进程,也是fork虚拟页表,再利用COW实现。但是RDB没有重写缓存池这种东西,也就是说,在写RDB期间新修改的不会被记录,会被视为下次RDB的工作。
RDB + AOF重写
RDB恢复数据很快,毕竟存的直接是物理量,但开销大,且存数据太少,不能像AOF重写一样将刷盘期间的新记录补上。
为了兼顾两者的的优点,可以先将RDB刷入AOF日志文件,然后将缓存区的内容存到后面。在作为一个新AOF文件覆盖旧的,实现混合持久化。
总结
其实有三种写,AOF本身的持久化,AOF的重写,RDB的快照写。
AOF本身的持久化开销小,有点类似redo刷盘,顺序写开销小。而重写开销和RDB差不多,这一点从他们两相似的工作流机制也能看出来。混合持久化的AOF 文件由 RDB 全量快照 + AOF 重写中增量命令 组成。和AOF自己的刷盘没什么关系,请不要弄混。
这里看出除了RDB和redo虽然记得都是"物理量",但是实际上和redo机制相似的反倒是AOF。