四. Redis 持久化
4.1 Redis 如何保证数据不丢失?
由于Redis的数据是保存在内存中,而内存中的数据会在Redis重启后丢失。因此,为了保证数据不丢失,Redis实现了数据持久化的机制。这个机制会将内存中的数据存储到磁盘,重启后可以从磁盘中恢复。
Redis共有三种数据持久化的方式:
- AOF日志:每执行一条写操作命令,就把该命令以追加的方式写到一个文件里;
- RDB快照:将某一时刻的内存数据,以二进制的方式写入磁盘;
- 混合持久化方式:Redis 4.0新增,集成了AOF和RBD的优点。
4.2 AOF 日志是如何实现的?
4.2.1 AOF 日志写入
Redis在执行完一条写操作命令后,就会把该命令以追加写的方式写到一个文件中。当Redis重启后,会读取该文件记录的命令,逐个执行来恢复数据。
为什么是先执行命令,然后再把命令写入日志?
Reids 是先执行写操作命令后,才将该命令记录到 AOF 日志里的,这么做其实有两个好处:
- 避免额外的检查开销:如果先写入再执行,那在写入的时候还无法确定是否能够执行。需要增加一步检查命令。
- 不会阻塞当前写操作命令的执行:因为是完成写操作再去写入之日。
也会存在风险:
- 数据可能会丢失:如果完成了写操作但还没来得及写入日志,服务器宕机了,会丢失这个数据;
- 可能阻塞其他操作:AOF日志也是在主线程中执行的,会阻塞后续的操作等待写入完成;
4.2.2 AOF 日志写回策略
Redis写入AOF日志的过程可以由下图所示:
- Redis执行完写操作命令后,会将命令追加到一个缓冲区;
- 然后通过write()系统调用,将该缓冲区的命令写入到磁盘中的AOF文件中。实际上这一步只会让命令拷贝到了AOF文件的内核缓冲区,什么时候写入到硬盘由内核决定;
Redis提供了3种写回硬盘的策略,控制什么时候将数据写入硬盘:
- Always:每次写操作执行完成后,直接将AOF日志数据写回硬盘;
- Everysec:写操作执行完成后,数据会先存放在AOF文件的内核缓冲区,每隔一秒将缓冲区的内容写回到磁盘。
- No:Redis不控制写回的执行,完全交给操作系统来控制。
4.2.3 AOF 重写机制
随着执行的写操作越来越多,AOF日志文件会越来越大,在数据恢复时会越慢,影响性能。因此,Redis提供了重写机制,来避免AOF日志文件越写越大。
AOF重写机制是根据K-V数据中的键值对,将每个键值对形成一个命令,记录到「新的 AOF 文件」中,完成后用这个「新的 AOF 文件」替换掉原有的AOF文件。
AOF重写机制的本质就是**只保留最新的。在使用重写机制后,就会读取 name 最新的 value(键值对) ,然后用一条 「set name xiaolincoding」命令记录到新的 AOF 文件,之前的第一个命令就没有必要记录了,因为它属于「历史」命令,没有作用了。这样一来,一个键值对在重写日志中只用一条命令就行了**。
AOF重写是怎么完成的?
Redis是在每次需要AOF重写时,通过**新开辟一个子进程bgrewriteaof
来完成AOF重写 **。这是由于:
- 不会阻塞主进程,主进程可以继续处理命令请求;
- 如果是多线程,由于共享数据,需要通过锁机制来保证数据安全,会影响性能。而子进程是拷贝数据的副本,创建子进程时,父子进程是共享内存数据的,不过这个共享的内存只能以只读的方式,而当父子进程任意一方修改了该共享内存,就会发生**「写时复制」,于是父子进程就有了独立的数据副本**,就不用加锁来保证数据安全。
数据不一致问题?
因为在重写过程中,主进程仍然可以正常处理命令。如果发生了对已有key-value的修改,由于**「写时复制」,子进程中关于这个key-value和主进程中的数据出现了不一致。为了解决这个问题,Redis设置了一个AOF重写缓冲区**。
在重写AOF期间,主进程对于所有的写操作命令除了将其加入到**「AOF 缓冲区」,还会加入到「AOF重写缓冲区」。当子进程完成重写时,会向主进程发送一个信号**(进程间的通信方式),主进程收到信号时会执行:
- 将**「AOF重写缓冲区」中的所有内容追加到「新的 AOF 文件」**中;
- 用**「新的 AOF 文件」**覆盖掉「旧的 AOF 文件」
截止到此,就完成了整个AOF重写的工作。
4.3 RDB 快照是如何实现的?
Redis使用AOF进行恢复时,如果AOF日志较多,需要逐个执行,势必会使Redis的恢复缓慢。为了解决这个问题,Redis增加了RDB快照。
RDB快照就是记录某一个瞬间的内存数据(是数据而不是命令),由于是直接记录的数据,因此在恢复时不需要执行操作命令,只需要将RDB文件读入内存即可,效率很高。
4.3.1 RDB快照生成方法
Redis提供了两个命令来生成RDB快照:save
和bgsave
,它们的区别在于生成快照是否在「主进程」里执行:
save
命令:在主进程中生成RDB快照,如果写入时间太长,会导致主线程阻塞,影响性能;bgsave
命令:会创建一个**子进程来生成RDB快照**,避免主线程的阻塞。
Redis 的快照是全量快照,也就是说每次执行快照,都是把内存中的**「所有数据」都记录到磁盘中。所以执行快照是一个比较重的操作,如果频率太频繁,可能会对 Redis 性能产生影响。如果频率太低,服务器故障时,丢失的数据会更多。**
4.3.2 RDB快照如何保证数据一致性?
如果是使用bgsave
命令来执行生成快照,此时主进程仍然可以继续处理操作,但是由于快照是在一瞬间的数据,所以不需要去关注是否数据发生了变化。因此,RDB快照不存在数据一致性问题。
如果主线程执行写操作,则被修改的数据会复制一份副本,然后 bgsave
子进程会**把原来副本数据写入 RDB 文件(不去管新的),在这个过程中,主线程仍然可以直接修改原来的数据。因此,借助写时复制技术(Copy-On-Write, COW)**,RDB不用关注数据一致性问题。
4.3 为什么会有混合持久化?
😊 RDB 优点是数据恢复速度快,但是快照的频率不好把握。频率太低,丢失的数据就会比较多,频率太高,就会影响性能。
😊 AOF 优点是丢失数据少,但是数据恢复不快。
为了集成了两者的优点, Redis 4.0 提出了混合使用 AOF 日志和RDB快照,也叫混合持久化,既保证了 Redis 重启速度,又降低数据丢失风险。
混合持久化是如何实现的?
-
使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据。
当开启了混合持久化,在AOF重写日志时,重写子进程就不是把数据转换成命令写入了,而是**直接生成当时数据库的RDB快照并写入到AOF文件中。然后,主进程中记录在AOF重写缓冲区的命令继续以AOF日志的格式写入到AOF日志文件中**,替换旧的的 AOF 文件。
混合持久化优点:
- 混合持久化结合了 RDB 和 AOF 持久化的优点,开头为 RDB 的格式,使得 Redis 可以更快的启动,同时结合 AOF 的优点,有减低了大量数据丢失的风险。
混合持久化缺点:
- AOF 文件中添加了 RDB 格式的内容,使得 AOF 文件的可读性变得很差;
- 兼容性差,如果开启混合持久化,那么此混合持久化 AOF 文件,就不能用在 Redis 4.0 之前版本了。
资料参考
内容大多参考自:图解Redis介绍 | 小林coding (xiaolincoding.com)