Redis在实际开发过程中是非常重要的,很多的应用场景都围绕它开展,我们分多节对实际开发过程中用到的场景进行详细整理介绍,并不是为了凑章节数量,而是引起大家对redis的重视程度。本节介绍数据持久化原理。
1、redis持久化
Redis持久化方案分为RDB和AOF两种。
(1)RDB
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。是默认的持久化方式。默认的文件名为dump.rdb。
可以通过配置设置自动做快照持久化的方式。
save 900 1 #900秒内如果超过1个key被修改,则发起快照保存save 300 10 #300秒内容如超过10个key被修改,则发起快照保存save 60 10000rdbcompression yesrdbchecksum yes
client 也可以使用save或者bgsave命令通知redis做一次快照持久化。
- save命令:阻塞当前Redis服务器,直到RDB过程完成为止,对于内存比较大的实例会造成长时间阻塞,线上环境不建议使用。
- bgsave命令:Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。
RDB文件保存过程
-
redis调用fork,现在有了子进程和父进程。
-
父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数 据是fork时刻整个数据库的一个快照。
-
当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。
优势
-
一旦采用该方式,整个Redis数据库将只包含一个文件,非常方便进行备份。
-
方便备份,可以很容易的将一个个RDB文件移动到其他的存储介质上
-
RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
-
RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。
劣势
-
如果需要尽量避免在服务器故障时丢失数据,那 RDB 不适合。 虽然 Redis 允许设置不同的保存点(save point)来控制保存 RDB 文件的频率, 但是, 因为RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。 因此可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 就可能会丢失好几分钟的数据。
-
每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU 时间非常紧张的话,那这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ,但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。
以下场景会自动触发RDB持久化:
-
使⽤save相关配置,如“save m n”。表示m秒内数据集存在n次修改时,⾃动触发bgsave。
-
如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点
-
执行debug reload命令重新加载Redis时,也会自动触发save操作
-
默认情况下执行shutdown命令时,如果没有开启AOF持久化功能则自动执行bgsave
(2)AOF
redis会将每一个收到的写命令都通过write函数追加到文件中(默认是 appendonly.aof)。 当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
当然由于os会在内核中缓存 write做的修改,所以可能不是立即写到磁盘上。这样aof方式的持久化也还是有可能会丢失部分修改。不过可以通过配置文件告诉redis通过fsync函数强制os写入到磁盘的时机。有三种方式如下(默认是:每秒fsync一次)
appendonly yes //启用aof持久化方式
# appendfsync always //每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
appendfsync everysec //每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐
# appendfsync no //完全依赖os,性能最好,持久化没保证
理论上当我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。会导致持久化文件会变的越来越大。
事实上为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis将使用与快照类似的方式将内存中的数据 以命令的方式保存到临时文件中,最后替换原来的文件。
1)所有的写入命令会追加到aof_buf(缓冲区)中。
2)AOF缓冲区根据对应的策略略向硬盘做同步操作。
3)随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。
4)当Redis服务器重启时,可以加载AOF文件进行数据恢复。
需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
RDB和AOF如何选择?
一般来说, 如果想达到足以媲美数据库的数据安全性,应该同时使用两种持久化功能。在这种情况下,当Redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,因为 AOF 文件保存的数据集要比RDB文件保存的数据集要完整。
如果可以接受数分钟以内的数据丢失,可以只使用 RDB 持久化。
不推荐只使用 AOF 持久化方式,因为定时生成 RDB 快照非常便便于进行数据备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快,除此之外,使用 RDB 还可以避免 AOF 程序的bug。
如果只需要数据在服务器运行的时候存在,也可以不使用任何持久化方式。
2、混合持久化
重启 Redis 时很少使用 RDB 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 RDB 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。

在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。
3、数据恢复
把RDB或者AOF文件拷贝到Redis的数据目录下,如果使用AOF恢复,配置文件开启AOF, 然后启动redis-server即可。
Redis 启动时加载数据的流程:
1. AOF持久化开启且存在AOF文件时,优先加载AOF文件。
2. AOF关闭或者AOF文件不存在时,加载RDB文件。
3. 加载AOF/RDB文件成功后,Redis启动成功。
4. AOF/RDB文件存在错误时,Redis启动失败并打印错误信息。
下一节整理redis集群和消息队列。
欢迎大家关注我的Java知识体系专栏,该专栏会持续更新,从原理角度覆盖Java知识体系的方方面面。
一文吃透JAVA知识体系(面试题)https://blog.csdn.net/wuxinyan123/category_7521898.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=7521898&sharerefer=PC&sharesource=wuxinyan123&sharefrom=from_link