Java中高级工程师必备知识点---Redis 中的两种持久化方式

 本教程整理了Java工程师必备基础知识,内容质量高,涉及知识面非常广,是广大中高级Java工程师必备开发手册,内容包括:

        一、Java基础知识;
        二、计算机网络知识;
        三、Linux;
        四、数据结构与算法;
        五、Mysql和Redis;
        六、Spring等框架知识;
        七、设计模式;
        八、分布式、微服务;
        九、常见面试问题解析;

内容太多,篇幅有限,完整版PDF可通过(PDF完整版)获取,本文摘取《9.2 Redis 中的两种持久化方式 》进行说明展示

9.2 Redis 中的两种持久化方式

9.2.1方式一:快照

Redis 快照 是最简单的 Redis 持久性模式。当满足特定条件时,它将生成数据集的时间点快照,例如, 如果先前的快照是在2 分钟前创建的,并且现在已经至少有 100 次新写入,则将创建一个新的快照。此 条件可以由用户配置 Redis 实例来控制,也可以在运行时修改而无需重新启动服务器。快照作为包含整 个数据集的单个 .rdb 文件生成。
但我们知道, Redis 是一个 单线程 的程序,这意味着,我们不仅仅要响应用户的请求,还需要进行内存 快照。而后者要求 Redis 必须进行 IO 操作,这会严重拖累服务器的性能。
还有一个重要的问题是,我们在 持久化的同时 内存数据结构 还可能在 变化 ,比如一个大型的 hash 字典正在持久化,结果一个请求过来把它删除了,可是这才刚持久化结束,咋办?
①、使用系统多进程 COW(Copy On Write) 机制 | fork 函数
操作系统多进程 COW(Copy On Write) 机制 拯救了我们。 Redis 在持久化时会调用 glibc 的函数
fork 产生一个子进程,简单理解也就是基于当前进程 复制 了一个进程,主进程和子进程会共享内存 里面的代码块和数据段:

 这里多说一点,为什么 fork 成功调用后会有两个返回值呢? 因为子进程在复制时复制了父进程的堆栈 段,所以两个进程都停留在了 fork 函数中 (都在同一个地方往下继续"同时"执行),等待返回,所以 次在父进程中返回子进程的 pid,另一次在子进程中返回零,系统资源不够时返回负数(伪代码如下)

pid = os.fork()
if pid > 0:
handle_client_request() # 父进程继续处理客户端请求
if pid == 0:
handle_snapshot_write() # 子进程处理快照写磁盘
if pid < 0:
# fork error
所以 快照持久化 可以完全交给 子进程 来处理, 父进程 则继续 处理客户端请求 子进程 做数据持久 化,它 不会修改现有的内存数据结构 ,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。但是 父进程 不一样,它必须持续服务客户端请求,然后对 内存数据结构进行不间断的修改
这个时候就会使用操作系统的 COW 机制来进行 数据段页面 的分离。数据段是由很多操作系统的页面 组合而成,当父进程对其中一个页面的数据进行修改时,会将被共享的页面复 制一份分离出来,然后 对这个复制的页面进行修改 。这时 子进程 相应的页面是 没有变化的 ,还是进程 产生时那一瞬间的数据。
子进程因为数据没有变化,它能看到的内存里的数据在进程产生的一瞬间就凝固了,再也不会改变,这 也是为什么 Redis 的持久化 叫「快照」的原因 。接下来子进程就可以非常安心的遍历数据了进行序列 化写磁盘了。

9.2.2 方式二:AOF

快照不是很持久 。如果运行 Redis 的计算机停止运行,电源线出现故障或者您 kill - 9 的实例意外发
生,则写入 Redis 的最新数据将丢失。尽管这对于某些应用程序可能不是什么大问题,但有些使用案例 具有充分的耐用性,在这些情况下,快照并不是可行的选择。
AOF(Append Only File - 仅追加文件 ) 它的工作方式非常简单:每次执行 修改内存 中数据集的写操作 时,都会 记录 该操作。假设 AOF 日志记录了自 Redis 实例创建以来 所有的修改性指令序列 ,那么就可 以通过对一个空的 Redis 实例 顺序执行所有的指令 ,也就是 「重放」 ,来恢复 Redis 当前实例的内存 数据结构的状态。
为了展示 AOF 在实际中的工作方式,我们来做一个简单的实验:
./redis-server --appendonly yes # 设置一个新实例为 AOF 模式
然后我们执行一些写操作:
redis 127.0.0.1:6379> set key1 Hello
OK
redis 127.0.0.1:6379> append key1 " World!"
(integer) 12
redis 127.0.0.1:6379> del key1
(integer) 1
redis 127.0.0.1:6379> del non_existing_key
(integer) 0
前三个操作实际上修改了数据集,第四个操作没有修改,因为没有指定名称的键。这是 AOF 日志保存 的文本:
$ cat appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
set
$4
key1
$5
Hello
*3
$6
append
$4
key1
$7
World!
*2
$3
del
$4
key1
如您所见,最后的那一条 DEL 指令不见了,因为它没有对数据集进行任何修改。
就是这么简单。当 Redis 收到客户端修改指令后,会先进行参数校验、逻辑处理,如果没问题,就 立即 将该指令文本 存储 AOF 日志中,也就是说, 先执行指令再将日志存盘 。这一点不同于 MySQL 、 LevelDB 、 HBase 等存储引擎,如果我们先存储日志再做逻辑处理,这样就可以保证即使宕机了,我 们仍然可以通过之前保存的日志恢复到之前的数据状态,但是 Redis 为什么没有这么做呢?
Emmm... 没找到特别满意的答案,引用一条来自知乎上的回答吧:
@ 缘于专注 - 我甚至觉得没有什么特别的原因。仅仅是因为,由于 AOF 文件会比较大,为了
避免写入无效指令(错误指令),必须先做指令检查?如何检查,只能先执行了。因为语法
级别检查并不能保证指令的有效性,比如删除一个不存在的 key 。而 MySQL 这种是因为它本
身就维护了所有的表的信息,所以可以语法检查后过滤掉大部分无效指令直接记录日志,然
后再执行。
①、 AOF 重写
Redis 在长期运行的过程中, AOF 的日志会越变越长。如果实例宕机重启,重放整个 AOF 日志会非常 耗时,导致长时间 Redis 无法对外提供服务。所以需要对 AOF 日志 " 瘦身 "
Redis 提供了 bgrewriteaof 指令用于对 AOF 日志进行瘦身。其 原理 就是 开辟一个子进程 对内存进 行 遍历 转换成一系列 Redis 的操作指令, 序列化到一个新的 AOF 日志文件 中。序列化完毕后再将操 作期间发生的 增量 AOF 日志 追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志 文件了,瘦身工作就完成了。
②、 fsync
AOF 日志是以文件的形式存在的,当程序对 AOF 日志文件进行写操作时,实际上是将内容写到了内核 为文件描述符分配的一个内存缓存中,然后内核会异步将脏数据刷回到磁盘的。
就像我们 上方第四步 描述的那样,我们需要借助 glibc 提供的 fsync(int fd) 函数来讲指定的文件
内容 强制从内核缓存刷到磁盘 。但 " 强制开车 " 仍然是一个很消耗资源的一个过程,需要 " 节制 " !通常 来说,生产环境的服务器,Redis 每隔 1s 左右执行一次 fsync 操作就可以了。
Redis 同样也提供了另外两种策略,一个是 永不 fsync ,来让操作系统来决定合适同步磁盘,很不安 全,另一个是 来一个指令就 fsync 一次 ,非常慢。但是在生产环境基本不会使用,了解一下即可。

9.2.3 Redis 4.0 混合持久化

重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重 放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费 很长的时间。
Redis 4.0 为了解决这个问题,带来了一个新的持久化选项 —— 混合持久化 。将 rdb 文件的内容和增量 的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是 自持久化开始到持久化结束 的 这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小:
于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。
  • 32
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java码仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值