redis - 持久化

redis持久化,就是要在内存上存数据,硬盘上也存数据。

当写数据时, 内存和硬盘同时写。

读数据时,读内存中的数据。

硬盘的数据是在redis重启时,用来恢复内存中的数据。(可以理解为redis内存数据的备份)

代价是消耗了更多的空间(同一份数据存储了两遍)

Redis ⽀持 RDB (Redis Database)和 AOF (Append Only File)两种持久化机制,持久化功能有效地避免因进程退出造成数据丢失问题, 当下次重启时利⽤之前持久化的⽂件即可实现数据恢复。

RDB策略是定期备份,而AOF策略是实时备份

RDB

RDB 持久化是定期 把当前进程数据生成快照 保存到硬盘的过程,触发 RDB 持久化过程分为手动触发自动触发

手动触发

⼿动触发分别对应 save 和 bgsave 命令:

• save 命令:阻塞当前 Redis 服务器,直到 RDB 过程完成为⽌,对于内存⽐较⼤的实例造成⻓间阻塞,基本不建议采⽤。

• bgsave 命令:Redis 进程执⾏ fork 操作创建⼦进程,RDB 持久化过程由⼦进程负责,完成后⾃动 结束。阻塞只发⽣在 fork 阶段,⼀般时间很短。(这里的bg就是background,就是在背后出来触发)

1、判断目前是否有多个客户端执行bgsava,如有多个就立即返回。因为都是保存的同一份数据,只执行一个bgsava 就行了

2、然后在fork出一个子进程出来(相当于redis server的克隆体,如果redis server中存的数据比较多,占用内存很大,fork利用写实拷贝的机制,也不会有过多内存的消耗。因为在bgsave这个场景中,绝大部分的内存数据是不需要改变的)

3、然后由子进程负责进行 写文件,生成快照过程。父进程继续接收客户端的请求,正常提供服务

4、子进程完成持久化之后,再通过信号通知父进程。父进程更新一些统计信息,然后子进程就可以销毁了

自动触发

在redis配置文件中设置一下,让redis每隔多长时间或者每产生多少次修改 就自动触发。

在目录 /etc/redis下的redis.conf配置文件中进行设置。

rdb镜像文件

redis生成的rdb文件,是存放在redis的工作目录中的。也就是在redis配置文件中进行设置的。

然后到这个目录下去,查看下

redis服务器是默认开启了rdb机制的。

这个rdb文件是一个二进制文件。把内存中的数据,以压缩的形式,保存到这个二进制文件中。这个尽量避免去修改rdb,如果改坏了, 当redis服务器重启之后,去读取rdb文件就会出现格式错误。

为此,redis提供了rdb校验工具

当执行 生成 rdb镜像 操作的时候,

1、会把要生成的快照数据,先保存至一个临时文件。当快照生成完毕

2、再删除之前的rdb文件

3、再把新生成的临时rdb文件改为刚刚的dump.rdb。

这样就保证了dump.rdb文件自始至终就只有一个

rdb演示

进入redis客户端,执行命令 redis-cli -h 127.0.0.1 -p 6379

然后查看rdb文件,执行命令 vi /var/lib/redis/dump.rdb

然后在redis客户端插入数据,再次查看rdb文件

此时rdb文件依旧和插入数据之前是一样的。因为现在并未执行手动触发rdb,也没有达到自动触发rdb的条件。

自动触发rdb的条件,也是在 目录 /etc/redis下的redis.conf配置文件中进行设置的。

也就是说配置自动触发的条件,就可以在redis.conf中进行配置。

但是生成一次rdb快照的成本是一个比较高的成本,不能让这个操作太频繁。

这也导致快照的数据和实时内存中的数据可能存在偏差。比如说在两次快照之间的时候,redis服务器挂了,就会导致在快照之间这个时间段的数据就丢失了,因为还并未写到磁盘。(AOF就是解决这种场景的有效方案)

操作1

操作:手动执行下sava(不推荐)或者bgsava,手动触发rdb

以为目前数据少,执行bgsave就立即完成了,立即查看应该就有结果。

但是当数据比较多的时候,执行bgsave可能就需要消耗一定时间,立即查看不一定就是生成完毕了的。

然后在查看rdb文件

虽然dump.rdb文件是二进制的,但是key还是能看出来的。

然后重启下redis,看下效果。

执行命令 service redis-server stop service redis-server start

进入redis客户端查看key

操作2

操作:插入新值,不手动执行bgsave

插入新值

查看rdb文件

并没有key1

然后重启redis,进入客户端查看是否还有新设置的值

查看rdb文件

redis生成快照操作,不仅仅是手动执行命令才触发,也能自动触发

自动触发的方式:

1、通过配置文件中的save 执行 M时间内,修改N次

2、通过shutdown 命令(redis里面的一个命令)关闭redis服务器,也会自动触发(service redis-server restart)(正常关闭

3、redis进行主从复制的时候,主节点也会自动生成rdb快照,然后再将快照文件内容传递给从节点

再设置新值,通过kill -9命令关闭redis,模拟redis server异常关闭

由于是由service启动的redis,ubuntu会有守护进程观察redis服务的情况,如果redis挂了,就会自动拉起一个新的。这里的进程id并不相同,也就是redis进行了重启过了

然后进入redis客户端

这里并没有刚刚的设置的key2。

总结

如果通过正常的流程重新启动redis服务器,此时redis会在退出的时候,自动触发生成rdb操作。但是如果是异常重启,比如 kill -9或者 服务器掉电,此时redis服务器来不及生成rdb文件,内存中尚未保存至快照的数据就会随着异常重启丢失掉。

操作3

操作:查看rdb文件的保存过程

先查看当前rdb文件信息,观察其中的inode编号,执行命令 stat dump.rdb

然后执行一次bgsave

再次查看rdb文件的inode编号

可以看到两次的inode编号并不相同,说明rdb文件已经不是之前的rdb文件了,而是更新后的。

文件的典型组织方式(ext4)主要是把整个文件系统分为了三个大的部分

1、超级块(存放的是整个文件系统的管理信息和元数据。)

2、inode区(存放着inode节点,每个文件都会分配一个inode数据结构,包括了文件的各种元数据,和该文件数据存储在哪些block中

3、block区(存放了文件具体的数据内容

操作4

操作:通过配置自动生成rdb快照

执行flushall 也会情况rdb文件。

修改 配置文件 /etc/redis/redis.conf

然后重启redis服务,配置才会生效

重启,执行命令 service redis-server restart

然后在redis客户端设置数据

然后1分钟之后,查看dump.rdb文件

可以看到RDB文件中已经把新增的key保存下来了

如果在配置文件中写入 save "",表示关闭自动触发RDB机制

操作5

操作:将rdb文件改坏,再通 kill -9重启redis

如果使用命令 service redis-server restart 重启redis,redis会在退出时,自动触发rdb快照,将改坏的rdb替换为正常的rdb文件了

改坏rdb文件

执行命令 kill -9重启redis

查看redis的日志,执行命令 vi /var/log/redis/redis-server.log

可以看到是因为恢复数据过程中,rdb文件读取错误。

总结

rdb改动的位置,如果在内容之前,redis启动不了。如果改在内容之后,redis是能启动的,数据也是能读到的。

但是rdb文件改动之后,就是一个错误文件,为此需要使用redis提供的工具进行检查rdb文件格式是否符合要求。

redis提供的工具在目录 /usr/bin下

检查工具和redis服务器,在5.0版本是同一个可执行程序,可以在运行时加入不同选项,而达到使用不同功能。

运行的时候,加入rdb文件作为命令行参数,此时就是以检查工具的方式来运行,而不会真的启动 redis 服务器

检查rdb文件,执行命令 redis-check-rdb dump.rdb

rdb的优缺点

• RDB 是⼀个紧凑压缩的⼆进制⽂件,代表 Redis 在某个时间点上的数据快照。⾮常适⽤于备份,全量复制等场景。⽐如每 6 ⼩时执⾏ bgsave 备份,并把 RDB ⽂件复制到远程机器或者⽂件系统中 (如 hdfs)⽤于灾备。

• Redis 加载 RDB 恢复数据远远快于 AOF 的⽅式

• RDB ⽅式数据没办法做到实时持久化 / 秒级持久化。因为 bgsave 每次运⾏都要执⾏ fork 创建⼦进 程,属于重量级操作,频繁执行成本过高。

• RDB ⽂件使⽤特定⼆进制格式保存,Redis 版本演进过程中有多个 RDB 版本,兼容性可能有风险。

• RDB最大的问题,不能实时的持久化保存数据。在两次生成快照之间,实时的数据可能会随着重启而丢失。

AOF

AOF(Append Only File)持久化:以独立日志的方式记录每次写命令,重启时再重新执行 AOF 文件中的命令达到恢复数据的目的。AOF 的主要作⽤是解决了数据持久化的实时性,⽬前已经是Redis 持久化的主流⽅式。理解掌握好 AOF 持久化机制对我们兼顾数据安全性和性能⾮常有帮助。

开启AOF的时候,RDB就不生效了。默认AOF是关闭状态,需要修改配置文件进行修改。

开启的话,就将no 改为 yes

appendfilename表示AOF生成的文件名,所在位置是个rdb文件目录一致,都是在 /var/lib/redis下,是可配置的。

配置完之后,需要重启使配置生效。执行命令 service redis-server restart

然后在客户端写入

再查看appendonlydir下的appendonly.aof.1.incr.aof文件

然后再通过kill -9的方式干掉redis重启,再通过redis客户端查看持久化的数据能否被恢复

AOF工作流程

redis是单线程服务器,但是速度很快,因为是操作内存。

但是引入AOF之后,既要写内存,又要写硬盘,还能保持之前的速度吗?

实际上,由于AOF 的工作流程,写磁盘的操作并不会影响redis的性能。AOF 的⼯作流程操作:命令写⼊(append)、⽂件同步(sync)、⽂件重写 (rewrite)、重启加载(load),

  1. 所有的写⼊命令会追加到 aof_buf(缓冲区)中。

  2. AOF 缓冲区根据对应的策略向硬盘做同步操作。

  3. 随着 AOF 文件越来越⼤,需要定期对 AOF 文件进⾏重写,达到压缩的目的。

  4. 当 Redis 服务器启动时,可以加载 AOF 文件进行数据恢复。

文件写入

硬盘上读写数据,顺序读写的速度是比较快的(相比内存还是慢得多),而随机访问则速度是比较慢的。

AOF是每次把新的操作写入到原有文件的末尾,属于是顺序写入

另外,写硬盘的时候,写入硬盘数据的多少,对于性能影响并不大,而写入硬盘的次数影响就比较大了。AOF是先把数据写入缓冲区,在根据一定策略写入硬盘,这样写入硬盘次数就大大减少了

数据写入缓冲区,本质还是写在内存中的,如果突然进程挂了,或者主机掉电了,缓冲区的数据就丢掉了。

文件同步

Redis 提供了多种 AOF 缓冲区同步⽂件策略,由参数 appendfsync 控制

|----------|-----------------------------------------------------|
| 可配置值 | 说明 |
| always | 命令写⼊ aof_buf 后调⽤ fsync 同步,完成后返回 |
| everysec | 命令写⼊aof_buf 后只执⾏ write 操作,不进⾏ fsync。每秒由同步线程进⾏fsync。 |
| no | 命令写⼊ aof_buf 后只执⾏ write 操作,由 OS 控制 fsync 频率。 |

系统调⽤ write 和 fsync 说明:

• write 操作会触发延迟写(delayed write)机制。Linux 在内核提供⻚缓冲区⽤来提供硬盘 IO 性 能。write 操作在写入系统缓冲区后立即返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区 页空间写满或达到特定时间周期。同步⽂件之前,如果此时系统故障宕机,缓冲区内数据将丢失。

• Fsync 针对单个⽂件操作,做强制硬盘同步,fsync 将阻塞直到数据写⼊到硬盘。

• 配置为 always 时,每次写⼊都要同步 AOF ⽂件,性能很差,在⼀般的 SATA 硬盘上,只能⽀持⼤ 约⼏百 TPS 写⼊。除⾮是⾮常重要的数据,否则不建议配置。

• 配置为 no 时,由于操作系统同步策略不可控,虽然提⾼了性能,但数据丢失⻛险⼤增,除⾮数据 重要程度很低,⼀般不建议配置。

• 配置为 everysec,是默认配置,也是推荐配置,兼顾了数据安全性和性能。理论上最多丢失 1 秒的 数据。

刷新策略频率越高 ,性能影响就越大 ,而数据可靠性就越高

刷新策略频率越低 ,性能影响就越小 ,而数据可靠性就越低

重写机制

随着命令不断写⼊ AOF,文件会越来越大,为了解决这个问题,Redis 引⼊ AOF 重写机制压缩文 件体积。AOF ⽂件重写是把 Redis 进程内的数据转化为写命令同步到新的 AOF ⽂件。

重写后的 AOF 为什么可以变小?有如下原因:

• 进程内已超时的数据不再写入文件。

• 旧的 AOF 中的⽆效命令,例如 del、hdel、srem 等重写后将会删除,只需要保留数据的最终版 本。

• 多条写操作合并为⼀条,例如 lpush list a、lpush list b、lpush list 从可以合并为 lpush list a b c。

较小的 AOF 文件一方面降低了硬盘空间占用,一方面可以提升启动 Redis 时数据恢复的速度。

AOF 重写过程可以手动触发和自动触发(配置文件中设置):

• 手动触发:调用 bgrewriteaof 命令。

• 自动触发:根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数确定⾃动触发时 机。

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

◦ auto-aof-rewrite-percentage:代表当前 AOF 占用大小相比较上次重写时增加的比例。

AOF重写流程如下

父进程fork子进程之后,子进程写aof文件的同时,父进程仍然不停地接收客户端的请求,并把这些请求写入到aof_buf缓冲区,再刷新到旧的aof文件中。

在fork子进程的一瞬间,子进程就继承了当初父进程的内存状态,因此子进程里的内存状态是父进程fork之前的状态,对于fork之后新来的请求,对内存造成的修改,子进程是不知道的。

为此,父进程准备了一个aof_rewrite_buf缓冲区,专门方fork之后接收的请求数据,子进程这边在把aof数据写入新AOF文件完成后,会通过信号通知父进程,父进程再将aof_rewrite_buf缓冲区中的内容也写入新的AOF文件中,然后新的AOF文件就可以替代旧的AOF文件了。

如果当前进程正在执⾏ AOF 重写,请求不执⾏。如果当前进程正在执⾏ bgsave 操作,重写命令 延迟到 bgsave 完成之后再执⾏。

重写的时候,不关心aof文件中原来都有什么,只是关系内存中最终的数据状态。

子进程只需要把内存中当前的数据获取出来,以AOF格式写入到一个新的AOF文件中。(内存中的数据状态,就相当于把AOF文件结果整理后的模样了)

思考:

1、RDB对于fork之后,就不对数据不管了,而AOF则是通过aof_rewrite_buf缓冲区的方式进行处理。为什么RDB不采用这样的策略?

RDB本身设计理念就是为了定期备份,而AOF则是实时备份。二者到底是谁更加实用高效呢?则应该划分不同的应用场景。RDB定期备份,对于redis性能影响不多,只有在备份的时候会产生影响,但是对于数据的可靠性就有一定的风险。而AOF虽然是实时备份,尽管采用了策略优化对redis的影响,但是实时备份,就意味着对于redis的影响是一直存在的,但对于数据的可靠性又是很友好的。

2、AOF重写机制fork之后,为什么还需要继续维护旧的aof文件,继续向aof_buf缓冲区写入文件。而不是交给子进程去写新的aof文件,父进程再将aof_rewrite_buf缓冲区的内容追加到新的aof文件,然后去替换旧的aof文件,还能省掉维护旧的aof文件的开销了?

考虑极端,如果子进程写aof文件写到一半,机器挂了,断网了,数据就丢掉了。而父进程继续通过向aof_buf缓冲区写入文件,刷新到旧的aof文件中,重启之后还能对数据进行恢复。

混合持久化

首先在redis客户端上设置数据

在查看aof文件

在redis客户端上执行命令,开启aof重写

再次查看aof文件,发现是和rdb文件一样都是二进制文件。

这是因为redis引入了"混合持久化"的方式,结合rdb和aof的特点。

aof本来是按照文本的方式写入文件的,但是文本的方式写文件,后续redis重启数据加载成本是比较高的。

引入混合持久化之后,按照aof的方式,每个请求和操作都记录入了文件,在触发aof重写之后,就会把当前内存的状态按照rdb的二进制格式写入到aof文件中,后续再进行操作,仍然按照aof文本的方式追加到文件后面(也就是前面是二进制,后续记录的请求操作仍然是文本)。

混合持久化的开启,也是在redis.conf文件中去设置的。默认是开启的。

当Redis上同时存在的rdb快照和aof文件的时候,是以aof文件为主(aof中包含的数据比rdb更全),下面是启动时数据恢复的一个流程。

总结:

  1. Redis 提供了两种持久化方案:RDB 和 AOF。

  2. RDB 视为内存的快照,产⽣的内容更为紧凑,占用空间较小,恢复时速度更快。但产生 RDB 的开销较大,不适合进行实时持久化,⼀般⽤于冷备和主从复制。

  3. AOF 视为对修改命令保存,在恢复时需要重放命令。并且有重写机制来定期压缩 AOF ⽂件。

  4. RDB 和 AOF 都使⽤ fork 创建⼦进程,利用 Linux 子进程拥有父进程内存快照的特点进行持久化, 尽可能不影响主进程继续处理后续命令。

相关推荐
&友情岁月&2 小时前
sql脚本的union的要注意点
数据库·sql
nvd112 小时前
基于 LangChain + Gemini + CloudSQL (pgvector) 的 RAG 实现指南
数据库·langchain
其美杰布-富贵-李2 小时前
Spring Data Redis + Redisson 学习笔记
redis·学习·spring
企鹅侠客2 小时前
第07章—实战应用篇:List命令详解与实战(下)
windows·redis·log4j·list
oMcLin2 小时前
Ubuntu 22.04 系统升级后 PostgreSQL 无法启动:如何解决数据库迁移中的兼容性问题
数据库·ubuntu·postgresql
福尔摩斯张2 小时前
STM32数码管和LCD显示技术深度解析(超详细)
数据库·stm32·单片机·嵌入式硬件·mongodb
公众号:ITIL之家2 小时前
服务价值体系重构:在变化中寻找不变的运维本质
java·运维·开发语言·数据库·重构
橙汁味的风2 小时前
《数据库系统概论》陈红、卢卫 - 11 - 数据库恢复技术
数据库·数据库系统概论
qq_455760853 小时前
redis - 事务
数据库·redis·缓存