【很干】Redis持久化机制原理分析~

Redis为什么要有持久化?

因为Redis是一个基于内存的数据库,支持高性能读写操作,但如果服务器故障重启,那么数据就会丢失,为了这个问题,故诞生了持久化机制。

不做持久化,redis宕机或重启后数据全部丢失。


Redis持久化方式?

  • RDB 持久化方式能够在指定的时间间隔能对你的数据进行快照存储.

  • AOF 持久化方式记录每次对服务器写的操作 ,当服务器重启的时候重新执行这些命令来恢复原始的数据 ,AOF命令以redis协议追加保存每次写的操作到文件末尾 . Redis还能对AOF文件进行后台重写 ,使得AOF文件的体积不至于过大.

如果同时使用 RDB 和 AOF 两种持久化机制,那么在 Redis 重启的时候,会使用 AOF来重新构建数据,因为 AOF 中的数据更加完整


RDB

可以通过命令创建RDB文件

  • save :会阻塞服务器进程直到RDB文件创建完成,阻塞期间客户端发送的所有请求都会被阻塞,创建完成后才会处理请求。

  • bgsavebgsavefork一个子进程去创建RDB文件,而父进程会继续处理命令请求

    • bgsave命令执行期间,对于客户端发来的 save命令或 bgsave命令会拒绝,这是为了防止父子进程同时去产生RDB文件,防止产生竞争。

    • 对于 bgsave 和 bgwriteaof 命令也不能同时执行,因为这两个命令都是依靠子进程来完成的,所以这两个命令只能按请求顺序执行。

我们还可以通过 Redis的配置文件来进行自动间隔保存。

save 900 2 服务器在900秒之内,对数据库至少进行了2次修改的话,就会执行 bgsave命令 save 300 20 服务器在300秒之内,对数据库至少进行了20次修改的话,就会执行 bgsave命令

只要满足上面条件中一个就会执行 bgsave命令。

自动间隔保存实现原理

对于配置文件来说,既然要在多少秒之内对数据库进行多少次的修改,那么对于Redis来说肯定是需要知道你修改的次数和时间, 而这修改的次数和时间在Redis中对于的就是 dirty计数器(记录修改的次数)和 lastsave(上一次进行保存的时间戳)

当服务器成功执行一个数据库修改命令之后,就会对dirty计数器进行更新,修改了多少次就增加多少

光有这两个还不够,计数器和时间戳只是起记录作用,Redis'中还有一个周期函数每个100毫秒执行,它其中一个功能就是检测配置文件中 save 选项条件是否满足,如果满足就执行 BGSAVE命令。

服务器在载入RDB文件时,会一直处于阻塞状态,知道载入完成。


AOF

与RDB不同,AOF是保存Redis服务器所执行的命令来记录数据库的状态的 与快照持久化相比,AOF 持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启:

appendonly yes

当AOF持久化功能打开时,服务器在执行完一个写命令之后,会以协议格式(纯文本格式)将被执行的写命令追加到服务器状态的 aof_buf缓冲区末尾。

在 Redis 的配置文件中存在三种不同的 AOF 持久化方式,它们分别是:

appendfsync always #将 aof_buf缓冲区的所有内容写入并同步到AOF文件 appendfsync everysec #将 aof_buf缓冲区的所有内容写入并同步到AOF文件,每秒执行一次 appendfsync no #将 aof_buf缓冲区的所有内容写入到AOF文件,但何时同步有操作系统决定。

AOF重写

目的:为了解决AOF文件膨胀的问题 例如:

RPUSH list "A" "B" RPUSH list "C" "D"

可以简化为:

RPUSH "A" "B" "C" "D"

使用 bgrewriteaof 来进行AOF重写

AOF在重写过程中,会对键进行检查,过期的键不会保存到重写后的 AOF文件中

bgrewriteaof 是创建子进程来进行AOF重写的,服务器父进程可以继续处理请求,但是这样可能就会出现数据不一致的情况,所以 Redis设置了AOF重写缓冲区,此时服务器在执行完一个写命令之后,会将这个写命令同时发给AOF 缓冲区和 AOF重写缓冲区 子进程在完成AOF重写之后会向父进程发送一个信号(==此时会阻塞父进程,其余都不阻塞==),父进程会将AOF重写缓冲区的所有内容写入到新的AOF文件中,此时会对新的AOF文件改名,原子性替换老的AOF文件


两种持久化机制优缺点

RDB优点

  • 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.

  • RDB文件是一个非常紧凑的文件,它保存了 Redis 在某个时间点上的数据集, 这种文件非常适合用于进行备份

  • RDB是一个紧凑的单一文件,很方便传送到云服务器上做备份,非常适用于灾难恢复.

  • RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.

RDB缺点

  • 如果redis宕机了,那么会丢失你指定时间间隔内的数据
  • DB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时

AOF优点

  • AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。

  • AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损。

AOF缺点

  • 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
  • 根据所使用的 fsync策略,AOF的速度可能会慢于RDB 。 在一般情况下,每秒fsync的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此
  • 在大数据量下,数据恢复较慢

两种持久化机制工作原理

RDB

当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:

  • Redis 调用forks. 同时拥有父进程和子进程。
  • 子进程将数据集写入到一个临时 RDB 文件中。
  • 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。

AOF 重写和 RDB 创建快照一样,都巧妙地利用了写时复制机制:

  • Redis 执行 fork() ,现在同时拥有父进程和子进程。
  • 子进程开始将新 AOF 文件的内容写入到临时文件。
  • 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
  • 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。
  • 搞定!现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。

RDB和AOF触发机制

RDB来说,提供了三种机制

  1. save:该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令
  2. bgsave:执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。
  3. 自动触发:自动触发是由我们的配置文件来完成的。

AOF也有三种触发机制

(1)每修改同步always:同步持久化 每次发生数据变更会被立即记录到磁盘 性能较差但数据完整性比较好

(2)每秒同步everysec:异步操作,每秒记录 如果一秒内宕机,有数据丢失

(3)不同no:从不同步


RDB和AOF区别

1、存储数据

RDB持久化保存键空间的所有键值对(包括过期字典中的数据),并以二进制形式保存,符合rdb文件规范,根据不同数据类型会有不同处理。

AOF持久化保存redis服务器所执行的所有写命令来记录数据库状态,在写入之前命令存储在aof_buf缓冲区。

2、持久化时间选择

RDB持久化通过conf的save选项设置持久化行为(单位时间内的修改次数)。

AOF持久化通过conf的appendfsync选项设置持久化行为(单位时间内的修改次数)。

3、数据还原

RDB持久化:服务器载入rdb文件,阻塞线程,在载入完成之前不接受任何命令。

AOF持久化:服务器创建不带网络连接的伪客户端,读取aof文件中的所有命令并执行(redis服务开启aof持久化在服务器启动时会选择aof文件恢复数据库状态)

4、过期键

RDB持久化在写入或读取时会忽略过期键

AOF持久化不会忽略过期键,在键被惰性删除或定期删除时向aof文件追加一条删除命令

5、文件大小

RDB持久化随着存储数据量的变化而变化(根据不同数据类型有不同的数据压缩优化)

AOF持久化随着执行命令的增加而增加(通过aof重写进行优化)


我是 Code皮皮虾 ,会在以后的日子里跟大家一起学习,一起进步! 觉得文章不错的话,可以在 掘金 关注我,这样就不会错过很多技术干货啦~

相关推荐
it噩梦2 小时前
Springboot 实现Server-Sent Events
java·spring boot·后端
鸽鸽程序猿2 小时前
【JavaEE】Spring Boot 项目创建
java·spring boot·java-ee
vampire-wpre2 小时前
SpringAOP
java
兩尛2 小时前
螺旋矩阵(java)
java·线性代数·矩阵
一路向北North4 小时前
Java使用replaceAll替换时不使用正则表达式
java·开发语言·正则表达式
yangfeipancc4 小时前
正则表达式
java·开发语言·正则表达式
Duck Bro5 小时前
MySQL:常用数据类型
java·数据库·c++·mysql·java-ee
double丶flower5 小时前
设置Mysql5.6允许外网访问
java·mysql
痞老板A小安装C45 小时前
Redis 过期策略和内存淘汰策略
数据库·redis·缓存
Cosmoshhhyyy6 小时前
Jackson库中JsonInclude的使用
java·开发语言