一文理解 Redis 持久化:RDB和AOF(redis持久化机制和如何实现持久化)
mhr18 2024-11-11 12:05 16 浏览 0 评论
1. 概述
我们知道 Redis 是一个内存数据库,也就意味着如果我们的电脑异常重启或者服务器宕机的情况下,存储在 Redis 中的数据会丢失。
Redis 虽然是个内存数据库,但是 Redis 支持 RDB 和 AOF 两种持久化机制,将数据写往磁盘,可以有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复。
RDB(Redis DataBase) 持久化是把当前进程数据生成 快照 保存到硬盘的过程。什么是快照?你可以理解成把当前时刻的数据拍成一张照片保存下来。
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是 默认 的持久化方式,这种方式就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为 dump.rdb 。
前面说了,RDB 是快照的方式来保存数据的,因此对于 RDB 来说,提供了两种触发机制, 手动触发和自动触发。
手动触发分别对应 save 和 bgsave 命令。
2.1 save
save 命令会阻塞当前 Redis 服务器,直到 RDB 过程完成为止。
因此,对于内存比较大的实例会造成长时间阻塞, 线上环境不建议使用 。
2.2 bgsave
bgsave 命令执行时,Redis 进程执行 fork 操作创建 的进程 ,RDB 持久化过程由子进程负责,完成后自动结束。阻塞只发生在 fork 阶段,一般时间很短。
显然 bgsave 命令是针对 save 阻塞问题做的优化。因此 Redis 内部所有的涉及 RDB 的操作都采用 bgsave 的方式。其具体流程如下:
- 执行 bgsave 命令,Redis 父进程判断当前是否存在正在执行的子进程,如 RDB/AOF 子进程,如果存在,bgsave 命令直接返回。
- 父进程执行 fork 操作创建子进程,fork 操作过程中父进程会阻塞,通过 info stats 命令查看 latest_fork_usec 选项,可以获取最近一个 fork 操作的耗时,单位为微秒。
- 父进程 fork 完成后, bgsave 命令返回 **Background saving started **信息并不再阻塞进程,可以继续响应其他命令。
- 子进程创建 RDB 文件,根据进程内存生成临时快照文件,完成后对原有文件进行原子替换。执行 lastsave 命令可以获取最后一次生成 RDB 的时间,对应 info 统计的 rdb_last_save_time 选项。127.0.0.1:6379> lastsave (integer) 1640574329
- 进程发送信号给父进程表示完成,父进程更新统计信息。
2.3 自动触发
除了执行命令手动触发之外,Redis 内部还存在自动触发 RDB 的持久化机制。自动触发是由我们的配置文件来完成的,在 redis.conf 配置文件中,有如下配置:
- savesave m n,指在 m 秒内,如果有 n 个键发生改变,则自动触发持久化(执行一次 bgsave 命令)。例如,save 60 1 则表明在 60 秒内,至少有一个键发生改变,就会触发 RDB 持久化。
- stop-writes-on-bgsave-error默认值为 yes。当启用了 RDB 且最后一次后台保存数据失败,Redis 是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了。
- rdbcompression默认值是 yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。
- dbfilename设置快照的文件名,默认是 dump.rdb。
- dir设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名,默认为 ./ 。
2.4 RDB 文件
RDB 生成的持久化文件默认为 dump.rdb ,可在配置文件中设置,也可以通过以下命令查找:
find / -name dump.rdb
# linux 下查找
[root@localhost ~]# find / -name dump.rdb
/usr/local/redis/redis-6.2.6/src/dump.rdb
RDB 文件保存在 dir 配置指定的目录下,文件名通过 dbfilename 配置指定。
可以通过执行 config set dir {newDir} 和 config set dbfilename {newFileName} 运行期动态执行,当下次运行时 RDB 文件会保存到新目录。
Redis 默认采用 LZF 压缩算法对生成的 RDB 文件做压缩处理,压缩后的文件远远小于内存大小,默认开启,可以通过参数 config set rdbcompression {yes|no} 动态修改。
虽然压缩 RDB 会消耗 CPU,但可大幅降低文件的体积,方便保存到硬盘或通过网维示络发送给从节点,因此线上建议开启。
如果 Redis 加载损坏的 RDB 文件时拒绝启动,并打印如下日志:
# Short read or 0OM loading DB. Unrecoverable error,aborting now.
这时可以使用 Redis 提供的 redis-check-dump 工具检测 RDB 文件并获取对应的错误报告。
2.5 RDB 的优缺点
2.5.1 优点
bgsave
2.5.2 缺点
- RDB 方式数据 没办法做到实时持久化/秒级持久化 。因为 bgsave 每次运行都要执行 fork 操作创建子进程,属于重量级操作,频繁执行成本过高。
- RDB 文件使用特定二进制格式保存,Redis 版本演进过程中有多个格式的 RDB 版本,存在老版本 Redis 服务无法兼容新版 RDB 格式的问题。
针对 RDB 不适合实时持久化的问题,Redis 提供了 AOF 持久化方式来解决。
AOF(Append Only File)持久化,以独立日志的方式 记录每次写命令 ,重启时再 重新执行 AOF 文件中的命令 达到恢复数据的目的。
AOF 的主要作用是解决了数据持久化的实时性,目前已经是 Redis 持久化的主流方式。相比 RDB 的全量备份节省了很多时间。
开启 AOF 功能需要设置配置,默认不开启。AOF 文件名通过 appendfilename 配置设置,默认文件名是 appendonly.aof。保存路径同 RDB 持久化方式一致,通过 dir 配置指定。
# 开启 AOF
appendonly yes
# AOF 文件名
appendfilename "appendonly.aof"
# always 每收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
# everysec 每秒强制写入磁盘一次,性能和持久化方面做了折中,推荐
appendfsync everysec
# 正在导出rdb快照的过程中,要不要停止同步aof
no-appendfsync-on-rewrite yes
# aof文件大小比起上次重写时的大小,增长率100%时,重写
auto-aof-rewrite-percentage 100
# aof文件,至少超过64M时,重写
auto-aof-rewrite-min-size 64mb
3.1 工作流程
AOF 的工作流程如下:
- 命令写入(append)所有的写入命令会追加到 aof_buf(缓冲区)中。
- 文件同步(sync)AOF 缓冲区根据对应的策略向硬盘做同步操作。
- 文件重写(rewrite)随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
- 重启加载(load)当 Redis 服务器重启时,可以加载 AOF 文件进行数据恢复。
3.2 命令写入
AOF 命令写入的内容直接是 RESP 文本协议格式。例如 set hello world 这条命令,在 AOF 缓冲区会追加如下文本:
* 3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
AOF 为什么直接采用文本协议格式?
文本协议具有很好的兼容性。开启 AOF 后,所有写入命令都包含追加操作,直接采用协议格式,避免了二次处理开销。文本协议具有可读性,方便直接修改和处理。
AOF 为什么把命令追加到 aof_buf 中?
Redis 使用单线程响应命令,如果每次写 AOF 文件命令都直接追加到硬盘,那么性能完全取决于当前硬盘负载。先写入缓冲区 aof_buf 中,还有另一个好处,Redis 可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。
3.3 文件同步
Redis 提供了多种 AOF 缓冲区同步文件策略,由参数 appendfsync 控制。主要有以下三种:
- always命令写入 aof_buf 后调用系统 fsync 操作同步到 AOF 文件,fsync 完成后线程返回命令 fsync 同步文件。配置为 always 时,每次写入都要同步 AOF 文件,在一般的 SATA 硬盘上,Redis 只能支持大约几百 TPS 写入,显然跟 Redis 高性能特性背道而驰,不建议配置。
- everysec写人 aof_buf 后调用系统 write 操作,write 完成后线程返回。操作由专门线程每秒调用一次 fsync 命令。配置为 everysec, 是建议的同步策略 ,也是默认配置,做到兼顾性能和数据安全性。理论上只有在系统突然宕机的情况下丢失 1 秒的数据。
- no写入 aof_buf 后调用系统 write 操作,不对 AOF 文件做 fsync 同步,同步硬盘操作由操作系统负责,通常同步周期最长 30 秒。配置为 no,由于操作系统每次同步 AOF 文件的周期不可控,而且会加大每次同步硬盘的数据量,虽然提升了性能,但数据安全性无法保证。
系统调用 write 和 fsync
write
操作会触发延迟写(delayed write)机制。Linux 在内核提供页缓冲区用来提高硬盘 IO 性能。write 操作在写入系统缓冲区后直接返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区页空间写满或达到特定时间周期。同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
fsync
针对单个文件操作(比如 AOF 文件),做强制硬盘同步,fsync 将阻塞直到写入硬盘完成后返回,保证了数据持久化。
3.4 文件重写
随着命令不断写入 AOF,文件会越来越大,为了解决这个问题,Redis 引入 AOF 重写机制压缩文件体积。AOF 文件重写是把 Redis 进程内的数据转化为写命令同步到新 AOF 文件的过程。
重写后的 AOF 文件为什么可以变小?原因如下:
- 进程内已经超时的数据不再写入文件。
- 旧的 AOF 文件含有无效命令,如 set a 111、set a 222 等。重写使用进程内数据直接生成,这样新的 AOF 文件只保留最终数据的写入命令。
- 多条写命令可以合并为一个,如:lpush list a、lpush list b、lpush listc 可以转化为:lpush list a b c。为了防止单条命令过大造成客户端缓冲区溢出,对于 list、set、hash、zset 等类型操作,以 64 个元素为界拆分为多条。
AOF 重写降低了文件占用空间,除此之外,另一个目的是:更小的 AOF 文件可以更快地被 Redis 加载。
AOF 重写过程可以手动触发和自动触发:
- 手动触发:直接调用 bgrewriteaof 命令。该命令会将内存中的数据以命令的方式保存到临时文件中,同时会 fork 出一条新进程来将文件重写。
- 自动触发:根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数确定自动触发时机。auto-aof-rewrite-min-size auto-aof-rewrite-percentage
3.5 重启加载
AOF 和 RDB 文件都可以用于服务器重启时的数据恢复。redis 重启时加载 AOF 与 RDB 的顺序是怎么样的呢?
- 当 AOF 和 RDB 文件同时存在时,优先加载 AOF;
- 若关闭了 AOF,加载 RDB 文件;
- 加载 AOF/RDB 成功,redis 重启成功;
- AOF/RDB 存在错误,启动失败打印错误信息。
3.6 AOF 的优缺点
3.6.1 优点
- AOF 可以更好的保护数据不丢失,一般 AOF 会每隔 1 秒,通过一个后台线程执行一次 fsync 操作,最多丢失 1 秒钟的数据;
- AOF 日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损;
- AOF 日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写;
- AOF 日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用 flushall 命令清空了所有数据,只要这个时候后台 rewrite 还没有发生,那么就可以立即拷贝 AOF 文件,将最后一条 flushall 命令给删了,然后再将该 AOF 文件放回去,就可以通过恢复机制,自动恢复所有数据。
3.6.2 缺点
- 对于同一份数据来说,AOF 日志文件通常比RDB数据快照文件更大。
- AOF 开启后,支持的写 QPS 会比 RDB 支持的写 QPS 低,因为 AOF 一般会配置成每秒 fsync 一次日志文件,当然,每秒一次 fsync,性能也还是很高的。
- 以前 AO F发生过 bug,就是通过 AOF 记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。
4. 如何选择
对于 RDB,它能够在指定的时间间隔对内存中的数据进行快照存储。
对于 AOF,他能记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF 命令以文本协议追加保存每次写的操作到文件末尾。Redis 还能够对 AOF 文件进行后台重写,使 AOF 文件体积不至于过大。
两种方式同时开启,在两种方式同时开启的情况下,Redis 启动的时候会优先加载 AOF 文件来恢复原始数据,因为在通常情况下 AOF 文件保存的数据集比 RDB 文件保存的数据集要完整,RDB 的数据会不实时。
那么只使用 AOF 呢?建议不要,因为 RDB 更适合用于备份数据库(AOF 在不断变化,不好备份),快速重启,而不会有 AOF 可能存在的潜在 bug,留着作为一个补救的手段。
当然,如果你只是用作缓存,只希望数据在程序运行的时候存在,那么就可以不使用任何持久化方式。
选项 | RDB | AOF |
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全 | 丢数据 | 三种策略 |
附:配置文件详解
# redis进程是否以守护进程的方式运行,yes为是,no为否(不以守护进程的方式运行会占用一个终端)。
daemonize no
# 指定redis进程的PID文件存放位置
pidfile /var/run/redis.pid
# redis进程的端口号
port 6379
#是否开启保护模式,默认开启。要是配置里没有指定bind和密码。开启该参数后,redis只会本地进行访问,拒绝外部访问。要是开启了密码和bind,可以开启。否则最好关闭设置为no。
protected-mode yes
# 绑定的主机地址
bind 127.0.0.1
# 客户端闲置多长时间后关闭连接,默认此参数为0即关闭此功能
timeout 300
# redis日志级别,可用的级别有debug.verbose.notice.warning
loglevel verbose
# log文件输出位置,如果进程以守护进程的方式运行,此处又将输出文件设置为stdout的话,就会将日志信息输出到/dev/null里面去了
logfile stdout
# 设置数据库的数量,默认为0可以使用select <dbid>命令在连接上指定数据库id
databases 16
# 指定在多少时间内刷新次数达到多少的时候会将数据同步到数据文件
save <seconds> <changes>
# 指定存储至本地数据库时是否压缩文件,默认为yes即启用存储
rdbcompression yes
# 指定本地数据库文件名
dbfilename dump.db
# 指定本地数据问就按存放位置
dir ./
# 指定当本机为slave服务时,设置master服务的IP地址及端口,在redis启动的时候他会自动跟master进行数据同步
replicaof <masterip> <masterport>
# 当master设置了密码保护时,slave服务连接master的密码
masterauth <master-password>
# 设置redis连接密码,如果配置了连接密码,客户端在连接redis是需要通过AUTH<password>命令提供密码,默认关闭
requirepass footbared
# 设置同一时间最大客户连接数,默认无限制。redis可以同时连接的客户端数为redis程序可以打开的最大文件描述符,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回 max number of clients reached 错误信息
maxclients 128
# 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key。当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory<bytes>
# 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no。
appendonly no
# 指定跟新日志文件名默认为appendonly.aof
appendfilename appendonly.aof
# 指定更新日志的条件,有三个可选参数 - no:表示等操作系统进行数据缓存同步到磁盘(快),always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全), everysec:表示每秒同步一次(折衷,默认值);
appendfsync everysec
相关推荐
- Spring Boot3 连接 Redis 竟有这么多实用方式
-
各位互联网大厂的后端开发精英们,在日常开发中,想必大家都面临过系统性能优化的挑战。当系统数据量逐渐增大、并发请求不断增多时,如何提升系统的响应速度和稳定性,成为了我们必须攻克的难题。而Redis,这...
- 隧道 ssh -L 命令总结 和 windows端口转发配置
-
摘要:隧道ssh-L命令总结和windows端口转发配置关键词:隧道、ssh-L、端口转发、网络映射整体说明最近在项目中,因为内网的安全密级比较高,只能有一台机器连接内网数据库,推送...
- 火爆BOOS直聘的13个大厂Java社招面经(5年经验)助你狂拿offer
-
火爆BOOS直聘的13个大厂Java社招面经(5年经验)助你狂拿offer综上所述,面试遇到的所有问题,整理成了一份文档,希望大家能够喜欢!!Java面试题分享(Java中高级核心知识全面解析)一、J...
- 「第五期」游服务器一二三面 秋招 米哈游
-
一面下午2点,35分钟golang内存模型golang并发模型golanggc原理过程channel用途,原理redis数据结构,底层实现跳跃表查询插入复杂度进程,线程,协程kill原理除了kil...
- RMQ——支持合并和优先级的消息队列
-
业务背景在一个项目中需要实现一个功能,商品价格发生变化时将商品价格打印在商品主图上面,那么需要在价格发生变动的时候触发合成一张带价格的图片,每一次触发合图时计算价格都是获取当前最新的价格。上游价格变化...
- Redis 中的 zset 为什么要用跳跃表,而不是B+ Tree 呢?
-
Redis中的有序集合使用的是一种叫做跳跃表(SkipList)的数据结构来实现,而不是使用B+Tree。本文将介绍为什么Redis中使用跳跃表来实现有序集合,而不是B+Tree,并且探讨跳跃表...
- 一文让你彻底搞懂 WebSocket 的原理
-
作者:木木匠转发链接:https://juejin.im/post/5c693a4f51882561fb1db0ff一、概述上一篇文章《图文深入http三次握手核心问题【思维导图】》我们分析了简单的一...
- Redis与Java整合的最佳实践
-
Redis与Java整合的最佳实践在这个数字化时代,数据处理速度决定了企业的竞争力。Redis作为一款高性能的内存数据库,以其卓越的速度和丰富的数据结构,成为Java开发者的重要伙伴。本文将带你深入了...
- Docker与Redis:轻松部署和管理你的Redis实例
-
在高速发展的云计算时代,应用程序的部署和管理变得越来越复杂。面对各种操作系统、依赖库和环境差异,开发者常常陷入“在我机器上能跑”的泥潭。然而,容器化技术的兴起,尤其是Docker的普及,彻底改变了这一...
- Java开发中的缓存策略:让程序飞得更快
-
Java开发中的缓存策略:让程序飞得更快缓存是什么?首先,让我们来聊聊什么是缓存。简单来说,缓存是一种存储机制,它将数据保存在更快速的存储介质中,以便后续使用时能够更快地访问。比如,当你打开一个网页时...
- 国庆临近,字节后端开发3+4面,终于拿到秋招第一个offer
-
字节跳动,先面了data部门,3面技术面之后hr说需要实习转正,拒绝,之后另一个部门捞起,四面技术面,已oc分享面经,希望对大家有所帮助,秋招顺利在文末分享了我为金九银十准备的备战资源库,包含了源码笔...
- “快”就一个字!Redis凭什么能让你的APP快到飞起?
-
咱们今天就来聊一个字——“快”!在这个信息爆炸、耐心越来越稀缺的时代,谁不希望自己手机里的APP点一下“嗖”就打开,刷一下“唰”就更新?谁要是敢让咱用户盯着个小圈圈干等,那简直就是在“劝退”!而说到让...
- 双十一秒杀,为何总能抢到?Redis功不可没!
-
一年一度的双十一“剁手节”,那场面,简直比春运抢票还刺激!零点的钟声一敲响,亿万个手指头在屏幕上疯狂戳戳戳,眼睛瞪得像铜铃,就为了抢到那个心心念念的半价商品、限量版宝贝。你有没有发现一个奇怪的现象?明...
- 后端开发必看!为什么说Redis是天然的幂等性?
-
你在做后端开发的时候,有没有遇到过这样的困扰:高并发场景下,同一个操作重复执行多次,导致数据混乱、业务逻辑出错?别担心,很多同行都踩过这个坑。某电商平台就曾因订单创建接口在高并发时不具备幂等性,用户多...
- 开发一个app需要哪些技术和工具
-
APP开发需要一系列技术和工具的支持,以下是对这些技术的清晰归纳和分点表示:一、前端开发技术HTML用于构建页面结构。CSS用于样式设计和布局。JavaScript用于页面交互和逻辑处理。React...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)