Redis数据丢失怎么办?一文给你讲解Redis的持久化机制

97 阅读7分钟

一、概述

1、持久化是什么?

Redis 是跑在内存里的,当程序重启或者服务崩溃,数据就会丢失,如果业务场景希望重启之后数据还在,就需要持久化,即把数据保存到可永久保存的存储设备中

2、持久化方式

Redis提供两种方式来持久化:

  1. RDB:记录redis某个时刻的全部数据,这种方式本质就是数据快照,直接保存二进制数据到磁盘,后续通过加载RDB文件恢复数据
  2. AOF:记录执行的每条命令,重启之后通过重放命令来恢复数据,AOF本质是记录操作日志,后续通过日志重放恢复数据

RDB是快照恢复,AOF是日志恢复,这是两者本质区别,但还有另外一些差别:

  • 体积方面:相同数据量下,RDB体积更小,因为是二进制紧凑型文件
  • 恢复速度:RDB是数据快照,可以直接加载,而AOF文件恢复,相当于重放情况,RDB显然会更快
  • 数据完整性:AOF记录了每条日志,RDB是间隔一段时间记录一次,用AOF恢复数据通常更为完整

二、RDB

1、怎么开启持久化?

配置语法:save interval num 表示间隔interval秒,至少有num条写数据操作

 save 900 1
 save 300 10
 save 60 10000

这三条是Redis默认存在的:说明redis默认开启了RDB持久化

2、什么时候持久化?

1、主动执行命令save

 127.0.0.1:6379 > save
 OK

save命令:主线程生成RDB文件,由于和执行操作命令在同一个线程,所以如果写入RDB文件的时间太长,会阻塞主线程

2、主动执行命令bgsave

 127.0.0.1:6379 > bgsave
 Background saving started

bgsave命令:创建一个子进程来生成RDB文件,这样可以避免主线程的阻塞

3、达到持久化配置

Redis配置持久化策略 也就是最开始介绍的开启持久化

周期函数会检查是否达到策略,到达就触发bgsave

 save 900 1
 save 300 10
 save 60 10000

4、在程序正常关闭的时候执行

在关闭时,Redis会启动一次阻塞式持久化(save),已记录更全的数据

3、RDB具体做了什么?

三件事:

  1. Fork出一个子进程专门做RDB持久化
  2. 子进程写数据到临时的RDB文件
  3. 写完之后,用新RDB文件替换旧的RDB文件

整体流程结构:

image-20240712175530000

因为是子进程进行save,所以执行RDB持久化过程中,Redis依然可以继续处理操作命令的,也就是数据是能被修改的,这就是写时复制技术实现的

具体而言:fork创建子进程之后,通过写时复制技术,子进程和父进程是共享同一块内存数据的,因为创建子进程的时候,会复制父进程的页表,但是页表指向的物理内存还是一个

image-20240712181039169

只有发生修改内存数据的情况时,物理内存才会被复制一份。

image-20240712181109409

就是这样,Redis使用bgsave对当前内存中的所有数据做快照,这个操作是由bgsave子进程在后台完成的,执行时不会阻塞父进程中的主进程,这就使得主线程可以同时修改数据

这样的目的是为了减少创建子进程时的性能损耗,从而加快创建子线程的速度,毕竟创建子进程的过程中,是可能阻塞主线程的(数据量非常大的时候)

可以看到,复制期间,读数据互相不影响,如果有写操作发生,则主进程复制一份内存,在这个复制的内存基础上,主进程在修改原来的数据,子进程持久化的依然是修改之前的数据

三、AOF

1、怎么开启 AOF

 appendonly yes

打开之后,Redis每条更改数据的操作都会记录到AOF文件中

当你重启后,AOF会助你重建状态,相当于请求全部重放一次,所以AOF恢复起来会比较慢

2、AOF写入流程

image-20240712182805511

Redis每条更改数据的操作都会记录到AOF文件中,会影响Redis的执行性能

但Redis实际是提供了不同的策略来选择不同程度的损耗,提供了3种刷盘策略:

  1. appendonly always:每次请求都刷入AOF;
  2. appendonly everysec:每秒刷一次盘;
  3. appendonly no:不主动刷盘,让操作系统自己刷,一般情况下linux会每30秒刷一次

image-20240712183229948

官方建议是 appendonly everysec,也就是每秒刷一次盘,同时崩溃时损失的数据只有1s,这大多数场景都是可以接受的

当然了,我们要根据实际业务来选择,如果不做什么超级热点缓存,就是简单的缓存,丢失30秒也不是什么大事,也可以选择 appendonly on

3、写入AOF细节

写入AOF,其实分了好几步

完整示意图:

image-20240712185435957

第一步:将数据写入AOF缓存中,这个缓存的名字是auf_buf,一个SDS数据

第二步:aof_buf对应数据刷入磁盘缓冲区;那什么时机执行呢?

  • 处理完时间处理后,等待下一次事件到来之前
  • 周期函数serverCron之后
  • 服务器退出之前的准备工作时
  • 通过配置指令关闭AOF功能时

第三步:刷盘,即调用系统的flush函数;根据不同的策略刷盘

  • 如果是appendfsync alaway策略,则立刻调用redis_fsync刷盘
  • 如果是aof_fsync_everysec策略,会用aof_background_fsync使用后台线程刷盘

4、AOF重写

aof是不断写入的,这容易带来一个疑问,如此下去aof不是会不断膨胀吗?

针对这个问题,Redis采用了重写的方式来解决:

Redis可以在aof文件提交变得过大时,自动地在后台fork一个子进程,专门对aof进行重写;针对于相同key的操作,进行合并,比如同一个key的set操作,那就是后面覆盖前面

在重写过程中,Redis不但将新的操作记录原有的aof缓冲区,而且还会记录在aof重写缓存区。一旦新aof文件创建完毕,Redis就会将重写缓冲区内容,追加到新的aof文件,再用新的aof文件替换原来的aof文件

image-20240712192402632

aof达到满足这两个条件则重写:

 # 相比上次重写时数据增长100%
 auto-aof-rewirte-percentage 100
 # 超过
 auto-aof-rewirte-size 64mb

在超过64m或相比上次重写时的数据大一倍,则触发重写,很明显,最后实际上还是在周期函数来检查和触发的

四、AOF优化-混合持久化

1、混合持久化是什么?

混合部署听名字似乎是同时开启RDB和AOF,实际上不是的;

混合部署时机发生在AOF重写阶段,将当前状态保存为RDB二进制内容,写入新的AOF文件,再将重写缓冲区的内容追加到新的AOF文件,最后替换原有的AOF文件

此时的aof文件,就不再单纯的是日志数据,而是二进制数据 + 日志数据的混合体,所以叫混合持久化

2、混合持久化解决什么问题?

aof文件内容从日志数据变成一部分体积更小的二进制数据 + 一部分aof日志数据;比常规的aof文件体积要小一些,恢复数据更快

3、怎么开启?

redis.conf

 aof-user-rdb-preamble

5.0之后默认打开的,所以5.0之后只要AOF配置开启,默认就是混合持久化

4、如何加载持久化数据

完整的具体加载流程如下:

image.png