Redis学习笔记(二) -- 持久化

以前学 Redis 只是停留在最简单的使用阶段,对其底层实现一点也不关心,甚至是配置文件的每个配置都不熟悉。o(︶︿︶)o 唉,近期开始恶补 Redis 的知识点,才发现不简单呀!

今天先讲解一下 Redis 的持久化!

为什么要持久化?
我们知道 Redis 是内存数据库,单线程性能也超级强。但是在现在来说,数据才是一切,有数据的公司和没数据的公司是无法相比的。偏偏 Redis 是内存数据库,所有的数据都存储的内存中,这带来高效率的访问同时,也有严重的缺陷。假设机房由于某种原因断电了,那所有的数据都没了,这是一笔不可估量的损失。
为了避免这种损失,肯定要将数据持久化到硬盘上。为此,redis 提供两种不同的持久化方式 RDB 和 AOF。

RDB 方式持久化
说到 RDB 的持久化,它是将数据写到硬盘里,本质上和我们的 Mysql 数据保存方式是一样的!但是 RDB 持久化生成的是一个经过压缩的二进制文件。该文件会在满足保存条件的情况下创建,在服务器重启的时候读取 RDB 文件中数据,将其还原到内存中!

RDB 文件创建
RDB 文件的创建有两种方式,一种是手动创建,一种是满足情况下的创建。

手动写入
手动创建的很简单,只要我们运行一下 save 命令就可以了!

save

如果写入成功后会 返回 OK ,结果如下:
save 命令提示
但是该命令可以说是被废弃的一个命令!首先,一旦运行该命令,会阻塞掉 Redis 服务器进程,一直阻塞到 RDB 文件写入完成为止。这就很恐怖了,前面我们说了 Redis 是单线程,这命令一执行,就占用了该线程,等于把进程都给阻塞了,如果数据量小,那还好,一旦数据量大,可能会导致整个服务不可用。

所以对于 save 命令,我们应该禁止在生产环境中使用。如果需要手动写入,可以使用 bgsave 命令来实现!

bgsave

命令执行后,结果如下:
bgsave命令提示
命令是如何执行的?(图片来自:https://www.cnblogs.com/kismetv/p/9137897.html)
在这里插入图片描述
save 命令直接堵塞父进程,而 bgsave 则会创建一个子进程,子进程完成 RDB 文件的创建,在创建完成的时候,会通知父进程。父进程在轮询等待子进程的通知的同时,也可以不断处理客户端发送的过来的命令。
注意:
bgsave 期间,不是所有命令后可以继续响应的,比如:save,bgsave 这两个命令。如果在 bgsave 期间也可以接收 save,那等于进程又 “死” 了。为了防止这种情况出现,所有在 bgsave 执行期间会拒绝这两个命令。

满足条件时创建
redis 配置文件中提供了几种条件执行保存的案例!如下:

save 900 1
save 300 10
save 60 10000

上述命令分别表示

  • 900 秒内至少发生过 1 次变化。
  • 300秒内至少发生过 10 次变化
  • 60 秒内至少发生过 10000 次变化

这里有个小坑点,如果不认真看的话,估计会给坑死。上面 save m n 的意思必须 n 和 m 都满足条件的情况下,才会调用 bgsave 创建 RDB 文件!

举个例子:
打王者时,有个任务是必须打够 10 把匹配且在线时间要超过 8 小时才算完成,我们很兴奋的花了 3 小时肝完 10 把,可任务却还没完成,需要继续等时间到!

这里的 save 也是一样的,即使我们 Redis 中某个 key, 70 秒钟完成了 100 次的变化,可是还是却不满足 300 秒,900 秒这两个条件,所以是不会进行 RDB 持久化的,除非 300 秒突然来了 9900 次修改,那就会创建了!

上面 save 操作涉及到键统计问题,统计一个键到底被修改了几次!下面我们来看看 Redis 中的计数器。

dirty 计数器和 lastsave 属性
上面说了要两种条件都满足的情况下才会执行 sava 操作。相对的,肯定需要有一个计数器来知道 Redis 中键被修改过几次。

  • dirty 计数器的目的是知道自上一次 save 或 bgsave 操作之后,到底键被修改过几次。
  • lastsave 属性用于记录上一次 save 或 bgsave 操作的时间!

RDB 文件结构

上面讲了那么多 RDB 持久化的知识点,接下来我们来看看 RDB 文件的结构。(图片来自:《Redis 设计与实现》)
在这里插入图片描述
我们可以看到结构 6 个字段,RDB 文件的的结构基本就是根据上面的格式构建的!
REDIS:
RDB 文件开头是 REDIS 部分,这个部分用了 5 个字节来保存 REDIS 这个 5 个字母。在 Redis 启动时会读取 RDB 文件,同时会进行文件的校验,而校验的便是这个这五个字母来判定是否为 RDB 文件!

db_version:
db_version 占用 4 个字节,用来存储 RDB 文件的版本号。

databases:
这个比较重要,如果对 Redis 较为熟悉,就知道 Redis 默认配置了 16 个数据库,每个数据库是隔离开的,数据不进行共享。默认情况下我们使用的是 0 号库,如果需要切换库可以使用如下方式:

select 1 #如果想其他数据库,可改为数字 1 - 15 中任意一个

EOF:
EOF 只占用一个字节,用于标志 RDB 文件的结束位置!

check_sum:
check_sum 校验文件是否出错或者损害,它是由前面几个部分结合计算出来的值。在 Redis 启动读取 RDB 文件时,会进行校验。

AOF 方式持久化
AOF 与 RDB 持久化不同,AOF 存储的不是数据,而是命令,我们从客户端发送到 Redis 服务端的命令都会被持久化到 AOF 文件中。

举个例子:
在这里插入图片描述
如上命令 RBD 是将键 key 和 值 123 持久化到 RDB 文件中。而 AOF 的形式却是将 set key 123 这个执行过的写命令保存到文件中。

开启 AOF 持久化
Redis 默认配置文件的情况下是关闭 AOF 的。如果需要打开需要将配置 appendonly nono 改成 yes

appendonly no --> appendonly yes

开启 AOF 后还需要看几个默认配置,这些将在后面解析!

AOF 持久化实现
AOF 持久化的步骤分为三个,追加(append)、写入、同步(sync)。

命令追加
当我们修改上面配置开启 AOF 的情况下,我们每次执行完一个写入命令,服务器就会将该命令追加到 aof_buf 缓冲区末尾。

写入与同步
redis.conf 配置文件中有配置了持久化的选项值,可以参考如下:

appendfsync 值行为
no将 AOF 缓冲区中所有内容写入 AOF 文件中,不立刻进行同步,何时同步交由操作系统来定
always将 AOF缓冲区中所有内容写入 AOF 文件并进行同步
everysec将 AOF 缓冲区所有内容写入 AOF,可以理解为每次有客户端发送写命令,都进行写入和同步。有个前提,必须大于1秒

上面三种情况各有各的优势。

appendfsync 值优势
no这个太难说了,交由操作系统来决定,完全不可控
always效率与另外两种相比最低,但是最安全,即使宕机也不会将数据丢失
everysec每隔一秒进行一个写入并同步,效率还是很不错的,即使出现宕机,也只是丢失一秒的数据

注:
最近小小学了下操作系统,为了提高效率,操作系统一般写入磁盘的数据都会先放到一个内存缓冲区,当缓冲区满了或时间到了才会将缓冲区内容真正写到磁盘上。假设突然宕机 – GG!当然,也有提供立刻刷新的函数,例如:fsyncfdatasync,执行他们会立刻将缓冲区内数据写入磁盘。具体也不太懂,C 渣渣 一个!

AOF 重写
由于每次执行完写命令都往缓冲区写入命令,这就会有一个情况,文件占用磁盘空间越来越大。假设我们是电商系统,用户每次下单都往缓存区写命令,不用多久,这个 AOF 文件可能会庞大到可怕的地步。不仅如此,如果我们要重启 Redis 服务器,从 AOF 读取命令也花费大量时间,十分不友好。

为了解决 AOF 文件庞大的问题,redis 提供 AOF 重写!重写的情况下回清楚掉冗余的命令,只保留下有用的命令。

举个例子:
在这里插入图片描述
往 list 里面 push 值 A B C,在弹出 A B 后只剩下 C,但是 AOF 持久化方式却都会把这 5 条命令都追加到缓冲区。而 AOF 重写之后,会将不必要的命令不写入新的 AOF 文件中。

如何写入呢?
根据上面的图,最后列表里只有 C 这个值,但是 AOF 却要使用 5 条命令来知道当前还有什么。而重写方式简单粗暴,直接读取当前 list 中剩余多少值,然后新命令就是 lpush list “C” 来替代上述 5 条命令!

lpush list “C”

这重写真的牛逼,一下子将多条才完成的情况变成一条 over

触发重写和 RDB 一样,有两种方式。一种是满足条件的自动触发!

手动触发 AOF
手动触发和 RDB 触发方式类似,不过执行的命令不同

bgrewriteaof

启动AOF
自动触发 AOF
自动 AOF 的话,我们需要先看看配置文件中默认使用的条件:

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

上述两行命令中,就是文件默认执行 AOF 重写的条件。但是这里也有个坑,可能也会把你坑的想哭!

auto-aof-rewrite-min-size 64mb 这行的意思是当文件大于 64m 的情况下,才开始重写 AOF。
auto-aof-rewrite-percentage 100 这行则表示本次大小和上一次大小的比例。

为什么说坑?因为这个和 save m n 一个样子,一定要两个条件都达成的情况下,才会执行 AOF 重写。
举个例子:
我们设置了重写的大小是 64m,之后 AOF 文件体积到了 64m,开始重写 AOF 文件。等文件下一次到 64m 时,它就不重写了,要比上一次重写的体积大一倍的情况下(也就是 128m),才进行重写。

AOF 重写流程图
AOF 文件的重写流程图(图片来自:https://www.cnblogs.com/kismetv/p/9137897.html)
AOF 重写流程图
当我们执行 bgrewirteaof 命令时,会创建一个子进程去进行 AOF 重写,父进程依旧可以接收客户端发送的命令。这就出现一个问题,在 AOF 期间,客户端可能不断在修改数据库的数据,导致即使 AOF 重写后数据不一致。

针对这个情况,Redis 服务设置了一个 AOF 重写缓冲区。客户端在发送过来的命令经过父进程处理后追加写到 AOF 重写缓冲区中和 AOF 缓冲区。这样一来,就可以保证 AOF 中的数据和我们 Redis 数据库中数据是一致的。当子进程 AOF 工作做完后,会通知父进程,父进程接到信后会执行一个处理函数,将新的 AOF 缓冲区中的命令写入新的 AOF 文件中,最后替换掉旧的 AOF 文件

AOF 重写缓冲区
(图片来自 《Redis 设计与实现》)
在这里插入图片描述
大致了解完 AOF 了,我们来看看 Redis 服务启动时,是如何加载 AOF 文件的

AOF 文件的载入与数据恢复
(图片来自:《Redis 设计与实现》)
在这里插入图片描述
上图的解析应该很详细了,总的来说就是循环执行一个个命令,知道 AOF 文件中保存的命令都被执行完毕,才正式启动完成 Redis 服务器。如果 AOF 文件很大,那恢复起来的速度是很感人的。

这里补充几个小知识点:

  • Redis 配置文件中默认不开启 AOF 持久化。但是如果 AOF 开启后,Redis 启动时,读取的是 AOF 文件,而不是 RDB 文件。只有当 AOF 持久化不开启的条件下,才会去读取 RBD 文件。
  • 上面说过 save 命令在执行时会拒绝其他 save,但是不会拒绝 bgrewirteaof 命令。当然,接收了该命令,aof 重写也不会立即开始,要得到 save 结束后,才进行重写。
  • AOF 或 RDB 文件启动时可能会出现加载失败的现象,这可能是文件出错或者损坏了。
    对于 AOF 文件,可以执行 redis-check-aof --fix aof 文件名 。对于 RBD 文件则执行 redis-check-rdb–fix rdb 文件名

最后:
关于 Redis 的持久化到这里我们就学完了。想大家最少也对 Redis 的持久化有了一定的了解,面试问到持久化也不用过于害怕,直接硬钢就是!当然,我们不能说哪种持久化方式好,每一种都有他的优势和劣势。方案是没有最完美的,只有最适当业务的方案就是好方案。

有兴趣的同学可以关注公众号一起学习
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值