四:AOF日志
原文:《04丨AOF日志:宕机了,Redis如何避免数据丢失?》
AOF日志记录了什么? => 操作的具体逻辑,具体格式见下图。
不同于redo log的WAL(写前日志),AOF日志是写后日志,先把数据写到内存中去,再写日志。为什么呢?因为为了避免额外的开销,Redis在向AOF里面记录日志的时候并不会对其进行语法检查,如果先记录日志再执行命令的话,如果语句有错,日志中就记录了错误的命令。除此之外,写后日志不会阻塞当前的写进程。
但是AOF也存在两个风险:
- 如果记录完日志还没有来得及持久化,就宕机了,那么数据就丢失了;
- 写后日志不会阻塞当前线程,但是可能会阻塞下一个线程。
要想解决这两个问题,就要考虑AOF日志的写盘时机,Redis下面有三种策略,我们根据需求来进行选择。
除此之外,AOF还有一个问题,那就是AOF文件过大会带来三个”性能“问题:文件系统本身对文件的大小有限制、文件太大时追加记录会变慢、发生宕机后恢复数据会很慢。 => AOF重写机制
**AOF重写机制:**把多条命令重写为一条,比如set key a, set key b, set key c => set key c, 只记录最终数据即可。而且,重写日志和AOF日志由主线程写回不同,重写过程是由后台线程bgrewriteaof来完成的,不会阻塞主线程。重写过程可以总结为”一个拷贝,两处日志“:
- 一个拷贝:每次重写时,主线程都会fork出后台的bgrewriteaof子进程,同时也会把主线程的内存拷贝一份给子进程,然后,bgrewriteaof子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志中;
- 两处日志:
- 第一处日志:主线程并未阻塞,此时新来的操作仍会写到主线程的AOF日志中去
- 第二处日志:是子进程的AOF重写日志,新来的操作也会写到这个AOF重写日志中;等到拷贝的数据都写完之后,这个AOF重写日志就会替代第一处的主线程日志了。
AOF重写过程中有没有其它潜在的阻塞风险?
- 风险一:Redis主线程fork创建bgrewriteaof子进程时,内核需要创建用于管理子进程的PCB,然后需要把主线程的PCB内容拷贝给子进程,这个创建和拷贝的过程是在内核中执行的,会阻塞主线程,而且,子进程要拷贝父进程的页表,这个过程也和Redis实例内存有关,如果实例内存越大,fork时间就越长;
- 风险二:bgrewriteaof子进程会和主线程共享内存,当主线程收到写操作时,会申请新的内存空间来进行操作,如果这个数据是bigkey,数据量比较大,就可能会因为申请大空间而造成阻塞,因为操作系统在分配内存时有查找和锁的开销,会导致阻塞。
总结:
AOF日志:逐一记录操作命令(写日志快),在恢复时再逐一执行命令;
其性能受写盘时机影响,三个写盘时机always、everysec、no,我们根据需求来选择;
为避免AOF日志过大而影响性能,有AOF重写机制,由后台线程bgrewriteaof执行,不会阻塞主线程。
但是,AOF恢复数据时太慢了!需要一条一条执行命令。
五:RDB日志
原文:《05丨内存快照:宕机后,Redis如何实现快速恢复?》
RDB方式,也即内存快照,把某一时刻的所有数据状态以文件的形式写到磁盘上。
1. 给哪些数据做快照?全量快照,所有数据。那么它会阻塞主线程吗?RDB文件生成的两种方式:
- save:在主线程中执行,会导致阻塞;
- **bgsave:**创建一个子进程,专门用于写入RDB文件,不会阻塞主线程。(默认)
2. 快照时数据能修改吗?
(Note:避免阻塞和正常处理写操作不是一回事!不阻塞可以执行读操作!)
肯定不能为了写操作而暂停快照,那么,这时有写操作怎么办? => **COW(CopyOnWrite)技术:**读操作不影响,写操作时,如果主线程要修改一块数据,那么这块儿数据就会被复制一份,生成该数据的副本(未被修改的吗?),bgsave子进程把该副本数据读入RDB文件中,而在这个过程中,主线程仍然可以对原数据正常进行写操作。
3. 多久做一次快照?
频繁的快照会带来两方面的开销:
- 磁盘压力大
- 但是fork创建子进程会阻塞主线程,频繁的fork会严重阻塞主线程
如果两次快照之间的数据变化量不大 => 增量快照。
增量快照:做了一次全量快照之后,后续的快照只对修改的数据进行记录即可,这么做的前提是“我们需要记住哪些数据修改了”,而如果数据变化量大的话,这些因为需要记录而产生的额外空间就会很大,就得不偿失了。
4. 混合使用AOF日志和RDB日志的方法!
AOF恢复数据速度太慢,RDB虽然恢复数据快,但是快照的频率不好把握,频率太低可能会丢失太多数据,fork频率太高又会阻塞主线程。
Redis 4.0提出了一种**混合使用AOF日志和RDB日志的方法:RDB以一定的频率执行,在两次RDB之间,使用AOF记录这期间的所有命令操作。**这样一来,快照就不用频繁的执行,避免了频繁fork对主线程的影响,而且AOF也只用记录两次快照之间的操作,不需要记录所有的操作,不会出现文件过大,避免了重写开销。
5. 持久化方法选择的建议:
- 数据不能丢失时,RDB和AOF混合使用;
- 允许分钟级别的数据丢失,可以只使用RDB;
- 如果只用AOF,建议使用everysec,因为它在可靠性和性能之间取了一个平衡。