Redis持久化原理

前言

Redis是内存数据库,为避免进程退出而导致数据的永久丢失,需要定期将数据以某种形式(命令或数据)持久化到硬盘中,当下次Redis重启时,利用持久化文件实现数据恢复。且出于容灾备份目的,可以把持久化文件拷贝到远程位置。

​ 从严格意义上讲,Redis共有四种持久化方案

 - 	 RDB,快照持久化,将当前进程中数据生成快照然后落盘
 - 	 AOF,将每次Redis执行的命令记录到单独文件中并保存
 - 	 虚拟内存(VM),Redis2.4版本后不再建议使用,3.2后更是完全找不到任何配置范例
 - 	 DISKSTORE,是一种存储设想,目前没有任何明确支持

不管是RDB还是AOF都是先写入一个临时文件,然后通过 rename 完成文件的替换工作。

Redis时间事件

https://kukudi.blog.csdn.net/article/details/95471990?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control

Redis的事件分为文件事件(File Event)和 时间事件(TimeEvent)

其中file event主要为网络事件而设计,而time event为一些后台事件设计。

现已知的时间事件就是定时任务ServerCron函数,默认每100ms执行一次,大致做了以下事情

  • 主动删除过期的键(也可以在读数据库时被动删除)
  • 喂看门狗 watchdog
  • 更新一些统计值
  • 渐进式rehash
  • 触发AOF 的重写操作(重写开启新进程)
  • 如果AOF正在重写,接受重写结束后的信号,将子进程里的临时aof文件写入磁盘,并将部分重写缓冲追加进aof文件
  • 触发AOF缓冲写入 内核/磁盘(刷新开启新线程)
  • 不同状态的client的超时
  • 复制重连
  • ……

RDB持久化

Redis按照一定的时间周期将目前服务中的所有数据全部写入到磁盘中,整体过程如下图所示。

Redis主配置文件的“REPLACTION”部分,放置了对这个过程的配置选项

image-20210131140249252

  1. Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof(后面会详细介绍该命令)的子进程,如果在执行则bgsave命令直接返回。bgsave/bgrewriteaof 的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。

  2. 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令

  3. 父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令

  4. 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换

  5. 子进程发送信号给父进程表示完成,父进程更新统计信息

触发条件

分为自动触发和手动触发

手动触发

save命令和bgsave命令

save命令会阻塞Redis服务进程,直到RDB文件创建完毕。在Redis服务器阻塞期间,服务器不能处理任何命令请求。

image-20210131132000003

而bgsave命令会创建一个子进程,由子进程负责创建RDB文件,父进程(Redis主进程)则继续处理请求。

image-20210131132344966

服务器日志如下

image-20210131132402890

bgsave命令执行过程中,只有fork子进程时会阻塞服务器,而对于save命令,整个过程都会阻塞服务器,因此save已基本被废弃,线上环境要杜绝save的使用。而在自动触发的过程中,Redis也会选择bgsave来进行持久化

自动触发

在配置文件中通过 save m n 来指定,在m秒内发生n次变化时,触发bgsave

image-20210131132538237

配置文件中写了三个触发bssave的规则,当满足任意一个条件时,都会触发bgsave

实现原理

Redis的save m n,是通过serverCron函数、dirty计数器、和lastsave时间戳来实现的。

  • serverCron是Redis服务器的周期性操作函数,默认每隔100ms执行一次;该函数对服务器的状态进行维护,其中一项工作就是检查 save m n 配置的条件是否满足,如果满足就执行bgsave。

  • dirty计数器是Redis服务器维持的一个状态,记录了上一次执行bgsave/save命令后,服务器状态进行了多少次修改(包括增删改);而当save/bgsave执行完成后,会将dirty重新置为0。

  • lastsave时间戳也是Redis服务器维持的一个状态,记录的是上一次成功执行save/bgsave的时间。

每隔100ms,执行一次serverCron函数,在serverCron函数中遍历 save m n配置条件,只要有一个条件满足,即

  • 当前时间 - lastsave > m
  • drity >= n

其他自动触发机制

除了save m n外,还有其他情况会触发bgsave

  • 主从复制场景下,如果从节点执行全量复制操作,则主节点会执行bgsave命令,并将rdb文件发送给从节点

  • 执行shutdown命令时,如果没有开启AOF,自动执行rdb持久化。

  • 执行debug reload

RDB文件

RDB文件是经过压缩的二进制文件,下面介绍关于RDB文件的一些细节。

  • 存储路径:通过redis.conf中dirdbfilename来指定,dir指定目录,dbfilename指定文件名。默认是redis根目录下的dump.db文件

Redis启动后也可以动态修改RDB存储路径,在磁盘损害或空间不足时非常有用;执行命令为config set dir {newdir}和config set dbfilename {newFileName}。

RDB文件格式

image-20210131142130867

  1. REDIS:常量,保存着”REDIS”5个字符。

  2. db_version:RDB文件的版本号,注意不是Redis的版本号。

  3. SELECTDB 0 pairs:表示一个完整的数据库(0号数据库),同理SELECTDB 3 pairs表示完整的3号数据库;只有当数据库中有键值对时,RDB文件中才会有该数据库的信息(上图所示的Redis中只有0号和3号数据库有键值对);如果Redis中所有的数据库都没有键值对,则这一部分直接省略。其中:SELECTDB是一个常量,代表后面跟着的是数据库号码;0和3是数据库号码;pairs则存储了具体的键值对信息,包括key、value值,及其数据类型、内部编码、过期时间、压缩信息等等。

  4. EOF:常量,标志RDB文件正文内容结束。

  5. check_sum:前面所有内容的校验和;Redis在载入RBD文件时,会计算前面的校验和并与check_sum值比较,判断文件是否损坏。

Redis默认采用LZF算法对RDB文件进行压缩。虽然压缩耗时,但是可以大大减小RDB文件的体积,因此压缩默认开启;可以通过命令关闭:config set rdbcompression no

需要注意的是,RDB文件的压缩并不是针对整个文件进行的,而是对数据库中的字符串进行的,且只有在字符串达到一定长度(20字节)时才会进行。

启动时加载

RDB文件的载入是服务器启动时自动执行的,没有专门的指令。但是由于AOF的优先级更高,因此当AOF开启时,Redis会优先载入AOF文件来恢复数据

只有当AOF关闭时,才会在Redis服务器启动时检测RDB文件,并自动载入。

服务器载入RDB文件期间处于阻塞状态,直到完成载入为止

常用配置

# bgsave自动触发的条件;如果没有save m n配置,相当于自动的RDB持久化关闭,不过此时仍可以通过其他方式触发
save m n:

# 当bgsave出现错误时,Redis是否停止执行写命令;设置为yes,则当硬盘出现问题时,可以及时发现,避免数据的大量丢失;设置为no,则Redis无视bgsave的错误继续执行写命令,当对Redis服务器的系统(尤其是硬盘)使用了监控时,该选项考虑设置为no
stop-writes-on-bgsave-error yes:

# 是否开启RDB文件压缩
rdbcompression yes:

# 是否开启RDB文件的校验,在写入文件和读取文件时都起作用;关闭checksum在写入文件和启动文件时大约能带来10%的性能提升,但是数据损坏时无法发现
rdbchecksum yes:

# RDB文件名
dbfilename dump.rdb:

# RDB文件和AOF文件所在目录
dir ./:

AOF持久化

AOF持久化,Append Only File,将Redis执行的每条写命令记录到单独的日志文件中,当Redis重启时,再次执行AOF文件中的命令来恢复数据。

相关配置

# 是否开启aof
appendonly yes

# 文件名称
appendfilename "appendonly.aof"

# 同步方式
appendfsync everysec

# aof重写期间是否同步
no-appendfsync-on-rewrite no

# 重写触发配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 加载aof时如果有错如何处理
aof-load-truncated yes

# 文件重写策略
aof-rewrite-incremental-fsync yes

# 执行AOF重写时,文件的最小体积,默认值为64MB。
auto-aof-rewrite-min-size 67108864

# 执行AOF重写时,当前AOF大小(即aof_current_size)和上一次重写时AOF大小(aof_base_size)的比值。
auto-aof-rewrite-percentage 100

appendfsync everysec 它其实有三种模式:

  • always:把每个写命令都立即同步到aof,很慢,但是很安全
  • everysec:每秒同步一次,是折中方案
  • no:redis不处理交给OS来处理,非常快,但是也最不安全

一般情况下都采用 everysec 配置,这样可以兼顾速度与安全,最多损失1s的数据。

aof-load-truncated yes 如果该配置启用,在加载时发现aof尾部不正确时,会向客户端写入一个log,但是会继续执行,如果设置为 no ,发现错误就会停止,必须修复后才能重新加载。

AOF重写的触发

手动触发: bgrewriteaof自动触发 就是根据配置规则来触发,当然自动触发的整体时间还跟Redis的定时任务频率有关系。

只有当auto-aof-rewrite-min-size和auto-aof-rewrite-percentage两个参数同时满足时,才会自动触发AOF重写,即bgrewriteaof操作。

AOF执行流程

下图是AOF重写的执行流程图

image-20210131170651801

  1. 命令追加写入

  2. 追加到aof_buf缓冲区

  3. 同步到磁盘(文件)

  4. AOF文件重写,定期重写AOF文件,达到压缩的效果。

重写是直接把当前内存的数据生成对应命令,并不需要读取老的AOF文件进行分析、命令合并。

AOF重写在Fork子进程时,会对主进程阻塞

从执行流程图中可以看出:

  1. 在重写期间,由于主进程依然在响应命令,为了保证最终备份的完整性,会把命令写入两个buf中,一个是aof_buf,这个缓冲区写入到旧的AOF文件;另一个是aof_rewrite_buf,这个缓冲区写入到新的AOF文件。
  2. 为了把重写期间响应的写入信息也写入到新的文件中,因此也会为子进程保留一个buf,防止新写的file丢失数据。这个新的缓冲区就是aof_rewrite_buf
  3. 重写是直接把当前内存的数据生成对应命令,并不需要读取老的AOF文件进行分析、命令合并。
  4. AOF文件直接采用的文本协议,主要是兼容性好、追加方便、可读性高可认为修改修复。

数据恢复

image-20210131181431622

启动时会先检查AOF文件是否存在,如果不存在就尝试加载RDB。那么为什么会优先加载AOF呢?因为AOF保存的数据更完整,通过上面的分析我们知道AOF基本上最多损失1s的数据。

性能实践

通过上面的分析,我们都知道RDB的快照、AOF的重写都需要fork,这是一个重量级操作,会对Redis造成阻塞。因此为了不影响Redis主进程响应,我们需要尽可能降低阻塞。

  1. 降低fork的频率,比如可以手动来触发RDB生成快照、与AOF重写;
  2. 控制Redis最大使用内存,防止fork耗时过长;
  3. 使用更牛逼的硬件;
  4. 合理配置Linux的内存分配策略,避免因为物理内存不足导致fork失败
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值