目录
一、Redis持久化
RDB
四种执行场景
底层执行原理
优缺点
AOP
三种fsync策略
AOF重写机制
工作基本流程
优缺点
RDB和AOF的对比
混合持久化
Redis 持久化的主要目的是为了确保数据的持久性和可靠性,避免因意外崩溃或重启导致的数据丢失。以下是一些进行 Redis 持久化的主要原因:
-
数据恢复:持久化可以让你在系统崩溃后恢复数据。例如,如果 Redis 服务器由于意外重启或故障而失去所有数据,持久化的快照(RDB 文件)或日志(AOF 文件)可以帮助你恢复到崩溃前的状态。
-
灾难恢复:通过定期备份持久化数据,可以在数据损坏或丢失的情况下快速恢复。尤其在生产环境中,这种备份机制非常重要。
-
数据共享:持久化的数据可以被用作其他系统或服务的输入,方便数据的共享和分析。
-
避免数据丢失:尽管 Redis 本身是一个内存数据库,但通过持久化,用户可以确保即使在故障情况下,重要数据不会丢失。
-
系统恢复时间:通过持久化,可以减少系统恢复时间,特别是在需要重新启动 Redis 实例时。持久化文件可以快速加载数据。
Redis 提供了两种主要的持久化机制:RDB(快照持久化)和 AOF(追加文件持久化),用户可以根据需求选择合适的方式或结合使用两者。
一、Redis持久化
RDB
RDB全称RedisDatabaseBackup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。
快照文件称为RDB文件,默认是保存在当前运行目录。快照持久化是 Redis 默认采用的持久化方式,
Redis 可以通过创建快照来获得存储在内存里面的数据在 某个时间点 上的副本。Redis 创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis 主从结构,主要用来提高 Redis 性能),还可以将快照留在原地以便重启服务器的时候使用。
四种执行场景
RDB持久化在四种情况下会执行:
- 执行save命令,可以立即执行一次RDB。save命令会导致主进程执行RDB,这个过程中其它所有命令都会被阻塞。只有在数据迁移时可能用到。
- 执行bgsave命令,这个命令执行后会开启独立进程完成RDB,主进程可以持续处理用户请求,不受影响。
-
Redis停机时会执行一次save命令,实现RDB持久化。
-
Redis内部有触发RDB的机制,可以在redis.conf文件中找到。当触发RDB条件时,也会执行RDB持久化。
# 900秒内,如果至少有1个key被修改,则执行bgsave , 如果是save "" 则表示禁用RDB
save 900 1
save 300 10
save 60 10000
底层执行原理
当执行BGSAVE命令时,Redis会创建一个子进程来执行持久化操作,而不会阻塞主线程的正常操作。
大致的执行流程是:Redis单独创建(fork)一个子进程进行持久化,先将数据写入到一个临时文件中,等到持久化过程都结束了在用这个临时文件替换上次持久化好的文件。
下面是具体的执行流程:
(1)BGSAVE开始时会fork主进程得到子进程,该子进程是父进程的完整副本,包括内存、文件描述符等信息,子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB 文件。
(2)那么fork是怎么实现的呢?
主进程实现对Redis数据的读写,但是在linux系统中所有的进程都没有办法直接操作物理内存,而是操作系统给每个进行分配一个虚拟内存,那么主进程只能操作虚拟内存,而后操作系统会维护一个虚拟内存与物理内存的映射关系表,这个表称为页表。所以主进程操作虚拟内存,虚拟内存基于页表的映射关系到物理内存,即真正的存储位置,这样就能实现对物理内存数据的读写了。
我们执行fork创建子进程的时候,不是把内存数据做拷贝,而是仅仅把页表也就是把映射关系拷贝给子进程。当子进程有了和主进程相同的映射关系,也就是最终一定能映射到相同的物理内存区域,这样就实现了子进程与主进程内存空间的共享。这样就无需拷贝内存中的数据,直接实现内存共享,这个速度是非常快的,这样阻塞的时间就尽可能的缩短了。
由于子进程共享内存空间和文件描述符,为了避免复制整个内存空间,操作系统使用Copy-On-Write(写时复制)机制,这意味着除非子进程试图修改与父进程共享的内存空间,否则它们会共享相同的内存页。
- 当主进程执行读操作时,访问共享内存;
- 当主进程执行写操作时,则会拷贝一份数据,执行写操作。
(3)这样子进程就可以开始执行BGSAVE操作了,遍历Redis内存中的数据结构,生成内存快照,并将其写入到临时的RDB文件中。在持久化操作完成后,子进程将临时的RDB文件保存到指定位置,通常是将其替换已有的RDB文件。
(4)BGSAVE操作完成后,子进程将退出。如果持久化操作完成成功,父进程会继续执行正常的操作。
子进程在写RDB的过程中,主进程可以接受用户请求去修改内存中的数据吗?可以。如果这个时候主进程在修改内存中的数据,子进程同时在读,那么读与写之间会有冲突发生,甚至有可能会出现一些脏数据。那怎么避免这个情况的发生呢?
所以为了避免这个情况的发生,fork采用Copy-On-Write(写时复制)技术,
- 当主进程执行读操作时,访问共享内存;
- 当主进程执行写操作时,则会拷贝一份数据,执行写操作。
通俗的说就是当主进程要写数据的时候,做一次拷贝。fork会把共享内存标记成只读模式readonly,任何一个进程都只能来读数据,不能来写数据。
现在假设真的接受了一个写的请求,那么主进程需要去到内存中写数据B,它必须先拷贝一份数据,也就是说把数据B完整的拷贝一份数据B副本,而后再去完成写操作。当然了,一旦完成拷贝,以后主进程在做读操作的时候,也要去数据B副本中去读了,也就是说页表映射关系已经发生 了变化。
每一次只要有写操作就拷贝,这样就可以避免这种问题了。
总的来说,BGSAVE开始时fork主进程的过程包括创建子进程、使用Copy-On-Write机制避免复制整个内存空间、执行持久化操作,最后子进程退出。这种方式可以确保持久化操作在后台异步执行,而不会阻塞主线程的正常操作。
但是在极端情况下可能发生这样一个情况,子进程写新RDB文件比较慢,耗时比较久,就在子进程写的过程中,主进程这边不断的在写操作,不断的在修改内存中的这些共享数据,结果就是所有的数据都被修改了一遍。那么这样是不是意味着所有的数据都要拷贝一份?理论上也就意味着redis对于内存的占用翻倍了。因此,Redis一般情况下都要预留一些内存空间。
优缺点
优点
-
高性能:RDB持久化在生成数据快照时,Redis只需将内存中的数据写入磁盘,因此可以快速完成。生成快照的过程中,Redis的主线程不会被阻塞。
-
简单易用:RDB持久化配置简单,可以通过配置文件进行设置,不需要复杂的操作。适合快速备份和恢复数据。
-
压缩存储:RDB文件通常是经过压缩的,因此存储空间效率较高,可以减少磁盘占用。
-
适合备份和数据恢复:由于RDB是数据的完整快照,便于在系统崩溃或数据损坏的情况下快速恢复。
-
支持持久化快照:可以在特定的时间间隔自动保存快照,支持基于时间的定期备份。
缺点
-
数据丢失风险:RDB持久化在每次生成快照时,如果Redis崩溃,则在上次快照后的数据可能会丢失。因此,数据的持久性不如AOF(Append Only File)高。
-
频繁生成快照的开销:虽然生成快照的性能较高,但在数据更新频繁的情况下,频繁的快照生成可能会消耗较多的CPU资源和I/O操作,导致性能下降。
-
耗时开销:fork子进程、压缩、写出RDB文件都比较耗时。
-
不支持增量备份:RDB文件是完整的快照,无法支持增量备份,意味着在恢复时需要完整的文件。
-
加载速度较慢:恢复数据时,RDB文件需要将整个快照加载到内存中,相比AOF逐条重放数据的方式,恢复速度可能会较慢,尤其是在数据集较大的情况下。
-
配置灵活性不足:RDB的持久化策略相对简单,缺乏对数据持久化的细粒度控制(如按数据类型、表等选择持久化策略)。
RDB持久化方式在性能和简易性方面表现优越,适用于对数据一致性要求不高、需要快速备份和恢复的场景。然而,对于对数据丢失敏感的应用场景,可能需要结合使用AOF等其他持久化方式,以提高数据安全性和一致性。
AOP
AOF全称为AppendOnlyFile(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件。与RDB存储某个时刻的快照不同,AOF是将客户端的每一个写操作命令都记录到日志中,追加到后缀为aof 的文件末尾,在Redis服务器重启时,会加载并运行aof文件的所有命令,以达到恢复数据的目的。
AOF默认是关闭的,需要修改redis.conf配置文件来开启AOF。
AOF的命令记录的频率也可以通过redis.conf文件来配。
三种fsync策略
在 Redis 的配置文件中存在三种不同的 AOF 持久化方式( fsync 策略),它们分别是:
- appendfsync always:主线程调用 write 执行写操作后,后台线程( aof_fsync 线程)立即会调用 fsync 函数同步 AOF 文件(刷盘),fsync 完成后线程返回,这样会严重降低 Redis 的性能(write + fsync)。
- appendfsync everysec:主线程调用 write 执行写操作后立即返回,由后台线程( aof_fsync 线程)每秒钟调用 fsync 函数(系统调用)同步一次 AOF 文件(write+fsync,fsync间隔为 1 秒)。
- appendfsync no:主线程调用 write 执行写操作后立即返回,操作系统会定期写到磁盘,这个频率往往会低一些。Linux 下一般为 30 秒一次(write但不fsync,fsync 的时机由操作系统决定)。
可以看出,这 3 种持久化方式的主要区别在于 fsync
同步 AOF 文件的时机(刷盘)。
为了兼顾数据和写入性能,可以考虑 appendfsync everysec
选项 ,让 Redis 每秒同步一次 AOF 文件,Redis 性能受到的影响较小。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis 还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。
AOF重写机制
因为是记录命令,AOF文件会比RDB文件大的多。而且AOF会记录对同一个key的多次写操作,只有最后一次写操作才有意义。为了解决AOF文件体积过大和只有最后一次写操作才有意义的问题,通过执行bgrewriteaof命令,让AOF文件执行重写功能,用最少的命令达到相同效果。
当 AOF 变得太大时(可以通过配置来控制触发条件),Redis会启动AOF重写过程。AOF重写不是简单地将原来的文件重写,而是创建一个新的AOF文件,其中包含一个更紧凑的操作序列。这个新文件仅包含重新生成当前数据库状态所需的最小命令集。
Redis也会在触发國值时自动去重写AOF文件。阈值也可以在redis.conf中配置。开启 AOF 重写功能,可以调用 BGREWRITEAOF
命令手动执行,也可以设置下面两个配置项,让程序自动决定触发时机:
auto-aof-rewrite-min-size
:如果 AOF 文件大小小于该值,则不会触发 AOF 重写。默认值为 64 MB。auto-aof-rewrite-percentage
:执行 AOF 重写时,当前 AOF 大小aof_current_size和上一次重写时 AOF 大小aof_base_size的比值。如果当前 AOF 文件大小增加了这个百分比值,将触发 AOF 重写。将此值设置为 0 将禁用自动 AOF 重写。默认值为 100。
工作基本流程
AOF 持久化功能的实现可以简单分为 5 步:
- 命令追加(append):所有的写命令会追加到 AOF 缓冲区中。
- 文件写入(write):将 AOF 缓冲区的数据写入到 AOF 文件中。这一步需要调用
write
函数(系统调用),write
将数据写入到了系统内核缓冲区之后直接返回了(延迟写)。注意!!!此时并没有同步到磁盘。 - 文件同步(fsync):AOF 缓冲区根据对应的持久化方式(
fsync
策略)向硬盘做同步操作。这一步需要调用fsync
函数(系统调用),fsync
针对单个文件操作,对其进行强制硬盘同步,fsync
将阻塞直到写入磁盘完成后返回,保证了数据持久化。 - 文件重写(rewrite):随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
- 重启加载(load):当 Redis 重启时,可以加载 AOF 文件进行数据恢复。
也就是说,开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到 AOF 缓冲区 server.aof_buf
中,然后再写入到 AOF 文件中(此时还在系统内核缓存区未同步到磁盘),最后再根据持久化方式( fsync
策略)的配置来决定何时将系统内核缓存区的数据同步到硬盘中的。
只有同步到磁盘中才算持久化保存了,否则依然存在数据丢失的风险,比如说:系统内核缓存区的数据还未同步,磁盘机器就宕机了,那这部分数据就算丢失了。
优缺点
优点
-
数据完整性:AOF记录所有写操作,即使Redis崩溃或重启,数据也能通过AOF文件恢复到最近的状态。
-
较高的恢复速度:相较于RDB(快照持久化),AOF可以提供更快的数据恢复,因为AOF记录了所有操作,而不是只记录某一时刻的快照。
-
可配置的同步策略:AOF支持不同的同步策略(如每个写命令后同步、每秒同步和从不同步),可以根据性能和数据安全需求进行调整。
-
易于调试:AOF文件是一个可读的文本文件,可以通过简单的文本编辑器查看和修改,便于调试和故障排查。
-
灵活性:支持对AOF文件的重写(rewrite),可以减少文件的体积和提高加载性能。
缺点
-
性能开销:由于需要将每个写命令记录到文件中,尤其是在每个命令后同步的情况下,AOF可能会造成较大的性能开销。
-
文件体积较大:随着写操作的增加,AOF文件会变得越来越大,虽然可以通过重写来优化,但这仍然是一个需要考虑的问题。
-
数据不一致风险:在某些同步策略下,如果Redis崩溃,可能会导致最后几条写命令未被记录,从而造成数据丢失。
-
恢复速度问题:在AOF文件非常大的情况下,恢复数据的速度可能会变得较慢,因为需要逐条重放命令。
-
复杂性:与RDB相比,AOF的实现和管理更为复杂,需要定期重写以优化性能和减少文件大小。
AOF适合对数据一致性要求较高的场景,但在性能和文件管理方面需要进行权衡。选择持久化方式时,可以根据具体应用场景的需求来决定使用AOF、RDB或两者结合。
RDB和AOF的对比
RDB和AOF各有自己的优缺点,如果对数据安全性要求较高,在实际开发中往往会结合两者来使用。
混合持久化
Redis 4.0 对于持久化机制做了什么优化?由于 RDB 和 AOF 各有优势,于是,Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble
开启)。
如果把混合持久化打开,在重写AOF文件时,将RDB文件的快照与AOF的增量数据结合在一起存储。
- RDB部分:保存了一个数据的快照,这部分占用文件的前面部分。
- AOF部分:保存了从快照生成之后的增量数据(即自上次RDB快照生成后的所有命令),这部分附加在RDB数据之后。
好处:可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。
- 由于RDB部分的存在,恢复数据时只需从快照开始加载,因此恢复速度更快。
- AOF部分确保了自快照之后的所有操作都被记录,数据的持久化更为完整,减少了数据丢失的可能性。
缺点:AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。