redis学习之数据持久化,防止数据丢失_redis持久化防范-爱代码爱编程
Redis的高性能是因为它是一个基于内存存储计算的非关系型数据库,这就导致它存在一个严重的问题:一旦服务器宕机,内存中的数据将全部消失。
目前,Redis有两种持久化方式:
- AOF(Append Only File)日志
- RDB(Redis DataBase)快照
AOF实现及原理
Redis写日志的逻辑是先执行命令,把数据写入内存中,再执行写日志操作。而我们知道的MySQL的WAL(全称:Write-Ahead Logging)预写日志系统却是先写日志(undo log,redo log,binlog),再执行数据的实际写入,防止发生故障时进行恢复。
首先我们要知道,MySQL的写操作是随机IO,且是要写入磁盘的,比较耗性能,先写入日志,就变成了顺序写入,再有后台线程以异步的方式去更新数据,同时大大降低了IO的次数,从而提供性能。
而Redis本身就是往内存中写数据,那么AOF先执行命令后记日志的原因是什么呢?Redis是为了避免命令语句的正确性检查开销。要是当Redis先向AOF里面记录日志,且没有对这些操作命令做语法检查,那么记录的日志将是错误的操作命令。一是这根本是一条错误的无效命令,没有发生对应数据的增量或更改;二是当使用该日志恢复数据时,执行该条命令时就会发生错误。而这种先写后记录日志的方式,可以避免以上错误,只有当命令执行成功时,Redis才会去执行日志的操作,否者,会直接抛出异常。除此之外,我们知道Redis是“单线程”的,所以AOF还有一个好处:它是在执行完命令之后再记录日志,因此不会阻塞当前的写操作。
顺序执行的操作都会存在一个风险,如果刚执行完一个命令,还没写入,服务器就宕机了,那么这条数据就会无法恢复。同时写入操作也会带来一定的风险,当AOF日志在主线程中执行写入磁盘时,当磁盘写压力大时,写入变得很慢,从而影响后续的操作被阻塞。
对于上述问题,Redis给我们提供了三种写回策略
,通过配置项appendfsync决定。
- Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘。
- Everysec,每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘。
- No,操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。
三种写回策略,各有优缺点,总结如下表。
appendfsync配置项 | 写会时机 | 优点 | 缺点 |
---|---|---|---|
Always | 同步写回 | 可靠性高,数据基本不会丢失 | 每个性能都要落盘,性能较差 |
Everysec | 每秒写回 | 性能适中 | 宕机时丢失1s的数据 |
No | 操作系统控制写回 | 性能好 | 宕机时丢失离上次写回后的数据 |
同时AOF如其名,是以文件追加的方式写入,随着写入的命令越来越多,文件也会越来越大,这个时候,AOF重写机制
就上场了。简单来说,就是对旧的AOF日志文件进行压缩精简,把一些重复的操作以及最终目标数据一致的多条操作,多变一,精简成单条命令。
RDB实现及原理
AOF日志记录的是操作命令,日志一多,恢复数据时需要再次执行这些命令,会比较耗时,影响上层应用的正常使用。RDB相比AOF,记录的是内存中某一时刻的数据状态,而且RDB文件是一个二进制文件,
结构非常紧凑,可以加速数据恢复。
Redis提供了两个命令来生成RDB文件。
- save:在主线程中执行,会导致阻塞。
- bgsave:创建一个子进程,专门用来写入RDB文件,避免了主线程的阻塞,这也是Redis RDB文件默认的生成方式。
bgsave子线程是Redis主线程fork生成的,可以共享主线程的所有内存数据,若在快照执行期间内,数据被改动了,那该怎么办。为了快照而阻塞主线程的写操作,肯定是不可取的,这个时候,Redis会借助操作系统的COW(Copy-On-Write)写时复制。简单来说,就是主线程要修改一块数据是,这块数据会被copy一份,生成副本;然后,主线程在这个副本上对数据做修改操作;同时,bgsave子进程就可以继续读取原来的文件,写入到RDB文件中,从而避免了对主线程写操作的影响。
但是RDB频繁的执行快照,也会给系统带来压力。一发面是频繁的写操作,会给磁盘带来压力,频繁的快照可能导致上一个快照还没有落盘,下一个快照就又开始了,导致各种异常情况。另一方面是bgsave子线程虽然执行期间不影响主线程,但是它需要通过主线程fork操作创建出来,这个过程是在主线程中执行的,是会阻塞主线程的,频繁的快照,也会导致主线程执行频繁的fork操作,从而阻塞主线程的其他操作。
有什么好的办法来解决上述问题吗?结合AOF和RDB各自的优缺点,Redis4.0之后,可以混合使用二者。内存快照按一定的频率执行,两次快照之前,使用AOF来记录期间的操作命令。这样一来,快照不用很频繁地执行,也就避免了频繁 fork 对主线程的影响。而且,AOF 日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销。