redis 持久化 (真实工作中应用方案)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 为了防止数据丢失以及服务重启时能够恢复数据,Redis 支持数据的持久化,主要分为两种方式,分别是 RDB 和 AOF;那么实际场景该如何实现呢?以上就是对这次redis持久化机制再次学习的总结与分享,希望能够帮助大家解决一些实际开发中的问题,如果各位大佬有其他更好的看法与观点,欢迎评论区留言和私信。

CSDN话题挑战赛第2期
参赛话题:一起学Java

♨️本篇文章记录的为Redis知识中缓存穿透,缓存击穿,缓存雪崩,缓存预热中秒杀相关内容,适合在学Java的小白,帮助新手快速上手,也适合复习中,面试中的大佬🙉🙉🙉。
♨️如果文章有什么需要改进的地方还请大佬不吝赐教❤️🧡💛

👨‍🔧个人主页 : 阿千弟
💖点击这里👉👉👉: Redis专栏学习

最近在工作之余重新学习了redis的持久化机制,有了比之前更深刻的看法与见解,并对此进行了分析与总结,供各位朋友参考也希望与各位大佬探讨

在这里插入图片描述
@[toc]

一、redis 持久化

1. 简介

1.1. 为什么需要持久化?

为了防止数据丢失以及服务重启时能够恢复数据,Redis 支持数据的持久化,主要分为两种方式,分别是 RDB 和 AOF;
那么实际场景该如何实现呢?

二、持久化方式

2. RDB 持久化

RDB 就是 Redis DataBase 的缩写,中文名为 快照 / 内存快照 ,RDB
持久化是把当前进程数据生成快照保存到磁盘上的过程,由于是某一时刻的快照,那么快照中的值要早于或者等于内存中的值。

2.1. 触发方式

2.1.1.触发 rdb 持久化的方式有 2 种,分别是 手动触发自动触发

2.1.1. 手动触发

手动触发分别对应 savebgsave 命令

save 命令:阻塞当前 Redis 服务器,直到 RDB 过程完成为止,对于内存 比较大的实例会造成长时间阻塞,线上环境不建议使用

bgsave 命令:Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。阻塞只发生在 fork 阶段,一般时间很短

2.2. bgsave 的基本流程?

  • redis 客户端执行 bgsave 命令或者自动触发 bgsave 命令;
  • 主进程判断当前是否已经存在正在执行的子进程,如果存在,那么主进程直接返回;
  • 如果不存在正在执行的子进程,那么就 fork 一个新的子进程进行持久化数据,fork 过程是阻塞的,fork 操作完成后主进程即可执行其他操作,子进程与主进程共享内存空间;
  • 子进程先将数据写入到临时的 rdb 文件中,待快照数据写入完成后再原子替换旧的 rdb 文件;
  • 同时发送信号给主进程,通知主进程 rdb 持久化完成,主进程更新相关的统计信息(info Persitence 下的 rdb_* 相关选项)。
2.2.1 自动触发

在以下 4 种情况时会自动触发

  • redis.conf 中配置 save m n,即在 m 秒内有 n 次修改时,自动触发 bgsave 生成 rdb 文件;
  • 主从复制时,从节点要从主节点进行 全量复制时 也会触发 bgsave 操作,生成当时的快照发送到从节点;
  • 执行 debug reload 命令重新加载 redis 时也会触发 bgsave 操作;
  • 默认情况下执行 shutdown 命令时,如果没有开启 aof 持久化,那么也会触发 bgsave 操作;
    在这里插入图片描述

    2.3 redis.conf 中配置 RDB

    快照周期 :内存快照虽然可以通过技术人员手动执行 SAVE 或 BGSAVE 命令来进行,但生产环境下多数情况都会设置其周期性执行条件。
2.3.1 Redis 中默认的周期性设置
# Save the DB to disk.
#
# save <seconds> <changes>
#
# Redis will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# Snapshotting can be completely disabled with a single empty string argument
# as in following example:
#
# 关闭 RDB
# save ""
#
# Unless specified otherwise, by default Redis will save the DB:
#   * After 3600 seconds (an hour) if at least 1 key changed
#   * After 300 seconds (5 minutes) if at least 100 keys changed
#   * After 60 seconds if at least 10000 keys changed
#
# You can set these explicitly by uncommenting the three following lines.
# 如果 900秒 内有 1 条Key信息发生变化,则进行快照;
save 3600 1
# 如果 300 秒内有 10 条Key信息发生变化,则进行快照;
save 300 100
# 如果 60 秒内有 10000 条 Key 信息发生变化,则进行快照。
save 60 10000
# save 5 1

其它相关配置

# 文件名称
dbfilename dump.rdb

# 文件保存路径
dir /home/work/app/redis/data/

# 如果持久化出错,主进程是否停止写入
stop-writes-on-bgsave-error yes

# 是否压缩
rdbcompression yes

# 导入时是否检查
rdbchecksum yes

stop-writes-on-bgsave-error:上文提到的在快照进行过程中,主进程照样可以接受客户端的任何写操作的特性,是指在快照操作正常的情况下。如果快照操作出现异常(例如操作系统用户权限不够、磁盘空间写满等等)时,Redis 就会禁止写操作。这个特性的主要目的是使运维人员在第一时间就发现 Redis的运行错误,并进行解决。一些特定的场景下,您可能需要对这个特性进行配置,这时就可以调整这个参数项。该参数项默认情况下值为yes,如果要关闭这个特性,指定即使出现快照错误 Redis 一样允许写操作,则可以将该值更改为 no

rdbcompression:该属性将在字符串类型的数据被快照到磁盘文件时,启用 LZF 压缩算法。Redis 官方的建议是请保持该选项设置为 yes,因为 “it’s almost always a win”。

rdbchecksum:从 RDB 快照功能的 version 5 版本开始,一个 64 位的 CRC 冗余校验编码会被放置在 RDB文件的末尾,以便对整个 RDB 文件的完整性进行验证。这个功能大概会多损失 10% 左右的性能,但获得了更高的数据可靠性。所以如果您的Redis 服务需要追求极致的性能,就可以将这个选项设置为 no。

2.4. RDB 更深入理解

由于生产环境中我们为 Redis 开辟的内存区域都比较大(例如6GB),那么将内存中的数据同步到硬盘的过程可能就会持续比较长的时间,而实际情况是这段时间 Redis 服务一般都会收到数据写操作请求。那么如何保证数据一致性呢?

RDB 中的核心思路是 Copy-on-Write,来保证在进行快照操作的这段时间,需要压缩写入磁盘上的数据在内存中不会发生变化。在正常的快照操作中,一方面 Redis 主进程会 fork 一个新的快照进程专门来做这个事情,这样保证了 Redis 服务不会停止对客户端包括写请求在内的任何响应。另一方面这段时间发生的数据变化会以副本的方式存放在另一个新的内存区域,待快照操作结束后才会同步到原来的内存区域 。

在这里插入图片描述

在进行快照操作的这段时间,如果发生服务崩溃怎么办?

很简单,在没有将数据全部写入到磁盘前,这次快照操作都不算成功。如果出现了服务崩溃的情况,将以上一次完整的 RDB 快照文件作为恢复内存数据的参考。 在快照操作过程中不能影响上一次的备份数据。Redis 服务会在磁盘上创建一个临时文件进行数据操作,待操作成功后才会用这个临时文件替换掉上一次的备份。

可以每秒做一次快照吗?

如果频繁地执行全量快照,也会带来两方面的开销:

  • 一方面,频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环。
  • 另一方面,bgsave 子进程需要通过 fork 操作从主线程创建出来。虽然,子进程在创建后不会再阻塞主线程,但是,fork 这个创建过程本身会阻塞主线程,而且主线程的内存越大,阻塞时间越长。如果频繁 fork 出 bgsave 子进程,这就会频繁阻塞主线程了。

那么,有什么其他好方法吗?此时,我们可以做增量快照,也就是 AOF持久化,是指做了一次全量快照后,后续的快照只对修改的数据进行快照记录,这样可以避免每次全量快照的开销。这个比较好理解。

2.5. RDB 优缺点

  • 优点
    • RDB 文件是某个时间节点的快照,默认使用 LZF 算法进行压缩,压缩后的文件体积远远小于内存大小,适用于备份、全量复制等场景;(体积小)
      • Redis 加载 RDB 文件恢复数据要远远快于 AOF 方式;(速度快)
  • 缺点
    • RDB 方式实时性不够,无法做到秒级的持久化;(实时性不够)
    • 每次调用 bgsave 都需要 fork 子进程,fork 子进程属于重量级操作,频繁执行成本较高;(fork 成本高)
    • RDB 文件是二进制的,没有可读性,AOF 文件在了解其结构的情况下可以手动修改或者补全;(可读性不高)
    • 版本兼容 RDB 文件问题;(不兼容)

针对 RDB 不适合实时持久化的问题,Redis 提供了 AOF 持久化方式来解决

3. AOF 持久化

AOF 全称为 Append Only File(追加文件)。Redis 处理的每一个写命令都会记录在 AOF 文件,可以看做是命令日志文件。

AOF 日志采用写后日志,即先写内存,后写日志。

  • 避免额外的检查开销:Redis 在向 AOF 里面记录日志的时候,并不会先去对这些命令进行语法检查。所以,如果先记日志再执行命令的话,日志中就有可能记录了错误的命令,Redis 在使用日志恢复数据时,就可能会出错。
  • 不会阻塞当前的写操作

但这种方式存在潜在风险:

  • 如果命令执行完成,写日志之前宕机了,会丢失数据。

  • 主线程写磁盘压力大,导致写盘慢,阻塞后续操作。

3.1. 如何实现 AOF

AOF 日志记录 Redis 的每个写命令,步骤分为:命令追加(append)、文件写入(write)和文件同步(sync)。

命令追加 当 AOF 持久化功能打开了,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区。

文件写入和同步 关于何时将 aof_buf 缓冲区的内容写入 AOF 文件中,Redis 提供了三种写回策略:

# Redis supports three different modes:
#
# no: don't fsync, just let the OS flush the data when it wants. Faster.
# always: fsync after every write to the append only log. Slow, Safest.
# everysec: fsync only one time every second. Compromise.
#
# The default is "everysec", as that's usually the right compromise between
# speed and data safety. It's up to you to understand if you can relax this to
# "no" that will let the operating system flush the output buffer when
# it wants, for better performances (but if you can live with the idea of
# some data loss consider the default persistence mode that's snapshotting),
# or on the contrary, use "always" that's very slow but a bit safer than
# everysec.
#
# More details please check the following article:
# http://antirez.com/post/redis-persistence-demystified.html
#
# If unsure, use "everysec".

# appendfsync always
appendfsync everysec
# appendfsync no

Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;

Everysec,每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;

No,操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。

3.2. redis.conf 中 配置 AOF

默认情况下,Redis 是没有开启 AOF 的,可以通过配置 redis.conf 文件来开启 AOF 持久化,关于 AOF 的配置如下

# appendonly参数开启AOF持久化
appendonly no

# AOF持久化的文件名,默认是appendonly.aof
appendfilename "appendonly.aof"

# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的
dir ./

# 同步策略
# appendfsync always
appendfsync everysec
# appendfsync no

# 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

no-appendfsync-on-rewrite:always 和 everysec 的设置会使真正的 I/O 操作高频度的出现,甚至会出现长时间的卡顿情况,这个问题出现在操作系统层面上,所有靠工作在操作系统之上的 Redis 是没法解决的。为了尽量缓解这个情况,Redis 提供了这个设置项,保证在完成 fsync 函数调用时,不会将这段时间内发生的命令操作放入操作系统的 Page Cache(这段时间 Redis 还在接受客户端的各种写操作命令)。

aof-load-truncated :与载入 RDB 文件类似,Redis 载入 AOF 文件时,会对 AOF 文件进行校验,如果文件损坏,则日志中会打印错误,Redis 启动失败。但如果是 AOF 文件结尾不完整 (机器突然宕机等容易导致文件尾部不完整),且 aof-load-truncated 参数开启,则日志中会输出警告,Redis 忽略掉 AOF 文件的尾部,启动成功

3.3. 深入理解 AOF 重写

AOF 会记录每个写命令到 AOF 文件,随着时间越来越长,AOF 文件会变得越来越大。如果不加以控制,会对 Redis服务器,甚至对操作系统造成影响,而且 AOF 文件越大,数据恢复也越慢。为了解决 AOF 文件体积膨胀的问题,Redis 提供 AOF 文件重写机制来对 AOF 文件进行 “瘦身”。

Redis 通过创建一个新的 AOF 文件来替换现有的 AOF,新旧两个 AOF 文件保存的数据相同,但新 AOF 文件没有了冗余命令。

AOF 重写会阻塞吗?

AOF 重写过程是由后台进程 bgrewriteaof 来完成的。主线程 fork 出后台的 bgrewriteaof 子进程,fork 会把主线程的内存拷贝一份给 bgrewriteaof 子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof 子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。

所以 aof 在重写时,在 fork 进程时是会阻塞住主线程的。

AOF 日志何时会重写?

有两个配置项控制 AOF 重写的触发:

auto-aof-rewrite-min-size: 表示运行 AOF 重写时文件的最小大小,默认为 64MB。

auto-aof-rewrite-percentage: 这个值的计算方式是,当前 aof 文件大小和上一次重写后 aof文件大小的差值,再除以上一次重写后 aof 文件大小。也就是当前 aof 文件比上一次重写后 aof 文件的增量大小,和上一次重写后 aof 文件大小的比值。

重写日志时,有新数据写入,主进程是怎么做的?

重写过程总结为:“一个拷贝,两处日志”。在 fork 出子进程时的拷贝,以及在重写时,如果有新数据写入,主线程就会将命令记录到两个 aof 日志内存缓冲区中。如果 AOF 同步策略配置的是 always,则直接将命令写回 旧的日志文件 ,并且保存一份命令至 AOF重写缓冲区 ,这些操作对新的日志文件是不存在影响的。(旧的日志文件:主线程使用的日志文件,新的日志文件:bgrewriteaof 进程使用的日志文件)

而在 bgrewriteaof 子进程完成会日志文件的重写操作后,会提示主线程已经完成重写操作,主线程会将 AOF重写缓冲中的命令追加到新的日志文件后面。这时候在高并发的情况下,AOF 重写缓冲区积累可能会很大,这样就会造成阻塞,Redis 后来通过 Linux 管道技术让 aof 重写期间就能同时进行回放,这样 aof 重写结束后只需回放少量剩余的数据即可。

最后通过修改文件名的方式,保证文件切换的原子性。

在重写日志整个过程时,主线程有哪些地方会被阻塞?

  1. fork 子进程时,需要拷贝虚拟页表,会对主线程阻塞 。
  2. 主进程有 bigkey 写入时,操作系统会创建页面的副本,并拷贝原有的数据,会对主线程阻塞。
  3. 子进程重写日志完成后,主进程追加 aof 重写缓冲区时可能会对主线程阻塞。

4. 从持久化中恢复数据

其实想要从这些文件中恢复数据,只需要重新启动 Redis 即可。

  • redis 重启时判断是否开启 aof,如果开启了 aof,那么就优先加载 aof 文件
  • 如果 aof 存在,那么就去加载 aof 文件,加载成功的话 redis 重启成功,如果 aof 文件加载失败,那么会打印日志表示启动失败,此时可以去修复 aof 文件后重新启动
  • 若 aof 文件不存在,那么 redis 就会转而去加载 rdb 文件,如果 rdb 文件不存在,redis 直接启动成功
  • 如果 rdb 文件存在就会去加载 rdb 文件恢复数据,如加载失败则打印日志提示启动失败,如加载成功,那么 redis 重启成功,且使用 rdb 文件恢复数据

那么为什么会优先加载 AOF 呢?因为 AOF 保存的数据更完整,通过上面的分析我们知道 AOF 基本上最多损失 1s 的数据。

三、redis性能与实践

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

  • 降低 fork 的频率,比如可以手动来触发 RDB 生成快照、与 AOF 重写;
  • 控制 Redis 最大使用内存,防止 fork 耗时过长;
  • 使用 更强的硬件
  • 合理配置 Linux 的内存分配策略,避免因为物理内存不足导致 fork 失败。

在线上我们到底该怎么做?这里提供了一些的实践经验。

  • 如果 Redis 中的数据并不是特别敏感或者可以通过其它方式重写生成数据, 可以关闭持久化,如果丢失数据可以通过其它途径补回
  • 自己制定策略定期检查 Redis 的情况,然后可以手动触发备份、重写数据
  • 单机如果部署多个实例,要防止多个机器同时运行持久化、重写操作,防止出现内存、CPU、IO 资源竞争,让持久化变为串行
  • 可以加入 主从机器,利用一台从机器进行备份处理,其它机器正常响应客户端的命令
  • RDB 持久化与 AOF 持久化可以同时存在,配合使用

总结:

以上就是对这次redis持久化机制再次学习的总结与分享,希望能够帮助大家解决一些实际开发中的问题,如果各位大佬有其他更好的看法与观点,欢迎评论区留言和私信
在这里插入图片描述

如果这篇【文章】有帮助到你💖,希望可以给我点个赞👍,创作不易,如果有对Java后端或者对redis感兴趣的朋友,请多多关注💖💖💖
👨‍🔧个人主页:阿千弟
如果大家对redis相关知识感兴趣请点击这里👉👉👉redis专栏学习

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
16天前
|
NoSQL Redis 数据库
一文讲透彻Redis 持久化
一文讲透彻Redis 持久化
26 0
|
11天前
|
存储 缓存 NoSQL
【Go语言专栏】Go语言中的Redis操作与缓存应用
【4月更文挑战第30天】本文探讨了在Go语言中使用Redis进行操作和缓存应用的方法。文章介绍了Redis作为高性能键值存储系统,用于提升应用性能。推荐使用`go-redis/redis`库,示例代码展示了连接、设置、获取和删除键值对的基本操作。文章还详细阐述了缓存应用的步骤及常见缓存策略,包括缓存穿透、缓存击穿和缓存雪崩的解决方案。利用Redis和合适策略可有效优化应用性能。
|
1天前
|
存储 NoSQL Redis
Redis数据结构精讲:选择与应用实战指南
Redis数据结构精讲:选择与应用实战指南
11 0
|
1天前
|
存储 NoSQL 算法
Redis持久化&Redis主从
Redis持久化&Redis主从
10 0
|
2天前
|
缓存 NoSQL 关系型数据库
深入浅出Redis(四):Redis基于RDB、AOF的持久化
深入浅出Redis(四):Redis基于RDB、AOF的持久化
|
3天前
|
NoSQL 算法 关系型数据库
Redis持久化 RDB & AOF
Redis持久化 RDB & AOF
11 0
|
11天前
|
NoSQL Redis 数据库
|
12天前
|
监控 NoSQL 算法
深入剖析Redis哨兵模式的原理和应用
Redis的哨兵模式是实现高可用性和自动故障转移的机制,当主服务器故障时,哨兵能自动检测并进行故障转移,确保服务连续和稳定性。哨兵模式通过监控主从服务器状态、自动故障转移、防止数据不一致,提高容错能力和负载均衡,降低运维成本,实现高可用性。哨兵通过检测主观下线和客观下线状态,以及选举Leader Sentinel来协调故障转移。Raft算法在其中用于领导者选举和状态一致性。哨兵模式通过综合考虑多种因素选举新主服务器并执行故障转移,保障集群稳定运行。
47 0
深入剖析Redis哨兵模式的原理和应用
|
13天前
|
监控 NoSQL Serverless
Serverless 应用引擎产品使用之在Serverless 应用引擎中,无法连接外部Redis数据库如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
22 2
|
16天前
|
存储 缓存 NoSQL
Redis实现延迟任务的几种方案
Redis实现延迟任务的几种方案