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

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
日志服务 SLS,月写入数据量 50GB 1个月
简介: 为了防止数据丢失以及服务重启时能够恢复数据,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专栏学习

目录
相关文章
|
3月前
|
NoSQL 安全 关系型数据库
Redis:持久化的两种方式
Redis持久化机制主要包括RDB和AOF两种方式。RDB通过生成数据快照进行持久化,支持手动或自动触发,具有加载速度快、文件紧凑等特点,但无法实时保存数据。AOF则记录每个操作命令,保障数据更安全,支持多种写入策略,并可通过重写机制优化文件大小。两者各有优劣,常结合使用以兼顾性能与数据安全。
|
3月前
|
消息中间件 缓存 NoSQL
Redis各类数据结构详细介绍及其在Go语言Gin框架下实践应用
这只是利用Go语言和Gin框架与Redis交互最基础部分展示;根据具体业务需求可能需要更复杂查询、事务处理或订阅发布功能实现更多高级特性应用场景。
302 86
|
3月前
|
存储 监控 NoSQL
Redis高可用架构全解析:从主从复制到集群方案
Redis高可用确保服务持续稳定,避免单点故障导致数据丢失或业务中断。通过主从复制实现数据冗余,哨兵模式支持自动故障转移,Cluster集群则提供分布式数据分片与水平扩展,三者层层递进,保障读写分离、容灾切换与大规模数据存储,构建高性能、高可靠的Redis架构体系。
|
3月前
|
存储 缓存 NoSQL
Redis持久化深度解析:数据安全与性能的平衡艺术
Redis持久化解决内存数据易失问题,提供RDB快照与AOF日志两种机制。RDB恢复快、性能高,但可能丢数据;AOF安全性高,最多丢1秒数据,支持多种写回策略,适合不同场景。Redis 4.0+支持混合持久化,兼顾速度与安全。根据业务需求选择合适方案,实现数据可靠与性能平衡。(238字)
|
3月前
|
存储 缓存 监控
Redis分区的核心原理与应用实践
Redis分区通过将数据分散存储于多个节点,提升系统处理高并发与大规模数据的能力。本文详解分区原理、策略及应用实践,涵盖哈希、范围、一致性哈希等分片方式,分析其适用场景与性能优势,并探讨电商秒杀、物联网等典型用例,为构建高性能、可扩展的Redis集群提供参考。
191 0
|
5月前
|
NoSQL Java Redis
Redis基本数据类型及Spring Data Redis应用
Redis 是开源高性能键值对数据库,支持 String、Hash、List、Set、Sorted Set 等数据结构,适用于缓存、消息队列、排行榜等场景。具备高性能、原子操作及丰富功能,是分布式系统核心组件。
587 2
|
4月前
|
监控 NoSQL 关系型数据库
保障Redis与MySQL数据一致性的强化方案
在设计时,需要充分考虑到业务场景和系统复杂度,避免为了追求一致性而过度牺牲系统性能。保持简洁但有效的策略往往比采取过于复杂的方案更加实际。同时,各种方案都需要在实际业务场景中经过慎重评估和充分测试才可以投入生产环境。
261 0
|
6月前
|
存储 监控 NoSQL
流量洪峰应对术:Redis持久化策略与内存压测避坑指南
本文深入解析Redis持久化策略与内存优化技巧,涵盖RDB快照机制、AOF重写原理及混合持久化实践。通过实测数据揭示bgsave内存翻倍风险、Hash结构内存节省方案,并提供高并发场景下的主从复制冲突解决策略。结合压测工具链构建与故障恢复演练,总结出生产环境最佳实践清单。
195 9
|
6月前
|
NoSQL 网络协议 Java
【Azure Redis】Redis服务端的故障转移(Failover)导致客户端应用出现15分钟超时问题的模拟及解决
在使用 Azure Cache for Redis 服务时,因服务端维护可能触发故障转移。Linux 环境下使用 Lettuce SDK 会遇到超时 15 分钟的已知问题。本文介绍如何通过重启 Primary 节点主动复现故障转移,并提供多种解决方案,包括调整 TCP 设置、升级 Lettuce 版本、配置 TCP_USER_TIMEOUT 及使用其他 SDK(如 Jedis)来规避此问题。
226 1
|
7月前
|
NoSQL 算法 安全
redis分布式锁在高并发场景下的方案设计与性能提升
本文探讨了Redis分布式锁在主从架构下失效的问题及其解决方案。首先通过CAP理论分析,Redis遵循AP原则,导致锁可能失效。针对此问题,提出两种解决方案:Zookeeper分布式锁(追求CP一致性)和Redlock算法(基于多个Redis实例提升可靠性)。文章还讨论了可能遇到的“坑”,如加从节点引发超卖问题、建议Redis节点数为奇数以及持久化策略对锁的影响。最后,从性能优化角度出发,介绍了减少锁粒度和分段锁的策略,并结合实际场景(如下单重复提交、支付与取消订单冲突)展示了分布式锁的应用方法。
559 3