一、为什么Redis需要数据持久化?
一般 Redis 用于作为缓存的业务场景中,将后端数据库中的数据缓存在 Redis 中,这样可以在内存中高效的访问数据,但是需要注意的是,如果发生宕机,那么 Redis 中缓存的内存中的数据就会丢失,此时再次启动 Redis 的时候,可以将后端数据库中的数据重新恢复 Redis 中这些数据,这样就可以让 Redis 重新被使用,但是这样做就增大了后端数据库的访问,使得后端数据库的压力增大,这样远远不比从 Redis 中读取数据性能好。因此 Redis 中数据的持久化十分重要。而 Redis 实现数据的持久化又两种方式:AOF(日志)和 RDB(快照)。
二、AOF 日志如何实现的?
1、为什么 Redis 写日志的时机在操作之后?
● 一般的数据库中写入日志的方式采用 WAL 写前日志机制(Write Ahead Log),也就是在数据操作之前进行写日志的操作,但是 Redis 不同,它采用的是当数据操作之后再进行写日志的操作。
● 其原因是为了避免额外的检查开销,Redis 写日志前不会对命令进行检查,如果操作语法有误,或者是操作失败等情况都会导致该操作并未执行成功,如果此时在操作执行前进行写日志操作,那么就会导致日志记录与数据库本身存在误差,因此 Redis 选择当命令执行成功之后再记录到日志中就可以避免额外的机制对命令操作进行检查并事后进行命令是否执行成功进行判断,将判断结果作为是否进行日志回滚的依据。
● 同时将写日志操作选择在操作之后进行,就以避免对当前写操作的阻塞。
2、Redis 选择在操作之后写日志存在的两个弊端
由于 Redis 将写日志的操作放在操作成功之后来执行,但是也正是采取这样的写日志的时机带了两个弊端。
● 如果在 Redis 执行完一个写操作之后,在即将将该操作写入日志前发生了宕机,那么就会导致日志记录不完整,如果将 Redis 作为缓存,那么还可以之后从后端数据库中进行数据的恢复,但是如果将 Redis 作为数据库,那么就会因为该操作并没有记录在日志中,该操作无法用日志进行数据恢复,导致需要根据该日志进行数据恢复后地数据与当前数据库数据不一致有数据丢失的错误。
● 其次,将写日志操作放在前一个操作的后面执行,但是写日志操作也是主线程来进行,将日志写入磁盘时,如果此时磁盘的压力大,那么就会导致阻塞后面一个操作的执行。
3、Redis 提供的三种写日志策略
由于上面写日志中带来的两个弊端,因此 Redis 提供了适用于不同需求的三种日志写回策略。
写回策略 | 特点 |
---|---|
Always | 同步写回,当在执行完一个写操作之后,就立即同步的将操作记录写入到磁盘中 |
Everysec | 每秒写回,当执行完一个写操作之后,将该操作写入到 AOF 日志的内存缓存区中,然后每隔一秒,将 AOF日志的内存缓存区中的日志记录写回到磁盘当中。 |
NO | 系统控制写回,当执行完一个写操作之后,先将日志写入到 AOF 日志的内存缓存区中,之后将日志写回磁盘的操作由 Redis 交给系统来进行控制。 |
以上三种日志写回策略中并没有做到十全十美,因为为了保证日志不丢失的可靠性的保障和减少阻塞主线程的保障不能做到全部满足。
以下是三种策略的写回时机及其优缺点的详情,当需要一个高可靠性的应用场景的情况下,我们就需要考虑使用 Always 日志写回策略,如果对日志数据的可靠性要求不高,并且需要应用于高性能的应用场景中就可以考虑使用 NO 日志写回策略,而 Everysec 日志写回策略则是这两种策略的在可靠性及其性能之间取了个折中,在允许可能存在丢失少量的数据同时要求性能高的应用场景可以考虑使用 Everysec 策略。
策略 | 日志写回时机 | 优点 | 缺点 |
---|---|---|---|
Always | 同步写回 | 可靠性高,日志数据基本上不会发生丢失 | 由于每次执行一次写操作都一同将日志写入磁盘中,其性能的影响较大 |
Everysec | 每秒写回 | 可靠性稍高,同时避免了每次执行一次写操作都要进行一次日志写磁盘操作带来较大的性能影响 | 当发生宕机的时候仍然可能会存在丢失了一秒内的日志缓存中的日志数据 |
NO | 操作系统控制写回 | 性能好 将日志的写回控制交给操作系统,其 | 可能会导致发生宕机的时候丢失大量的数据 |
4、AOF 日志文件如果过大会带了什么影响?
当我们根据不同的业务场景选择合适的日志写回策略并不是高枕无忧了,当随着项目的运行时间的增长,而 AOF 日志的写入采用的是追加写的方式,当随着对 Redis 的键值对的操作越来越多的时候,就会导致其 AOF 日志文件越来越大,从而带了三个方面的性能影响。
● AOF 是以文件的形式保存的,然而文件系统中也会有对文件大小的约束,无法保存过大的文件。
● AOF 日志文件越大,当进行日志追加写操作就会随着文件大小越来越大其写操作效率越来越低。
● 当发生宕机的时候,我们就需要借助 AOF 日中中记录的操作完成数据的恢复,由于 AOF 日志文件过大,导致数据恢复的事件过长,使得 Redis 长时间被阻塞不能提供服务。
5、Redis 如何解决 AOF 文件过大问题?
Redis 采用日志重写机制来完成 AOF 日志文件的压缩,其中日志压缩的原理就是在原先的 AOF 日志文件中存储的的是整个项目中执行完成的所有操作过程,而新创建的 AOF 则根据当前数据库具体存储的键值对的值写成一个写操作的记录,从而实现将具体的某个键值对的新增及其之后的修改操作这些多个操作进行合并为最终的一个写操作,这样一来重写后的 AOF 文件中记录的操作记录的个数几乎就等于数据库中存储的总键值对的数量。
6、AOF 日志重写机制的原理过程
简单概括 AOF 日志重写过程:一个拷贝,两处日志。
● 当开启 AOF 日志重新的时候,主线程fork(分支出)后台的子进程 bgrewriteaof 来负责完成,这样就可以避免 AOF 重写过程阻塞主线程了。
● 首先主线程 fork 将当前的数据库中的数据“拷贝”一份到 bgrewriteaof 子进程中(复制的是主线程中数据块对应物理地址的页表),之后 bgrewriteaof 子进程将拷贝的数据库中的数据写成一个个的写操作记录到重写 AOF 日志中。
● 此时由于在 AOF 重写过程,但是当有新的数据操作命令的时候,Redis 仍然会在当前正在使用的 AOF 日志中进行操作,为了避免这些处于 AOF 重写过程中执行完成的最新的操作的日志不丢失,Redis 就额外的为主 AOF 日志开辟了一个 AOF 缓冲区(磁盘文件保存),这样即使在未完成 AOF 重写期间即使 Redis 宕机了,主 AOF 日志也不会丢失最新的操作日志的记录(这也是对原来的 AOF 日志继续保持处理)。除此之外,为了避免重写后的 AOF 日志丢失了这份最新操作的日志记录,Redis 除了往(AOF 日志缓冲区)写入以外还往(重写 AOF 日志缓冲区) 写入了,当 (重写 AOF 日志) 完成了之后,还会将 (重写日志缓冲区) 中的日志记录一同写入。这样子就可以避免了在日志重写期间发生数据丢失的风险。
7、AOF 重写过程会阻塞主线程吗?
虽然在重写 AOF 日志的过程大部分都是由子进程 bgrewriteaof 来完成,但是仍然存在阻塞主线程的情况。
● 主线程 fork 子进程 bgrewriteaof 的过程(Redis 数据库中的数据量越大, bgrewriteaof 复制主线程中数据块对应物理地址的页表消耗的时间越长,阻塞时间越长)
● 主线程将 AOF 日志重写期间将写操作记录到 AOF 重写日志缓存区中。
● 当 AOF 重写日志完成之后,主线程将 AOF 重写日志进行改名及其替代原来的 AOF 日志的过程。