redis持久化

相关阅读

持久化

Redis的数据时存在内存中的,当服务器宕机时,Redis中存储的数据就会丢失,为了保证数据在服务器重启后恢复,需要将内存中的数据持久化到硬盘上。

RDB(Redis DataBase)

RDB 是 Redis DataBase 的缩写,即内存块照。快照就是在某一时刻,将Redis中的所有数据,以文件的形式存储起来。这就类似于照片,当你给朋友拍照时,一张照片就能把朋友一瞬间的形象完全记下来。我们可以将快照复制到其它服务器从而创建具有相同数据的服务器副本。如果数据量很大,保存快照的时间会很长。如果系统发生故障,将会丢失最后一次创建快照之后的数据。使用时注意:

  • 全量快照

    Redis 的数据都在内存中,为了提供所有数据的可靠性保证,它执行的是全量快照,也就是说,把内存中的所有数据都记录到磁盘中,这就类似于给 100 个人拍合影,把每一个人都拍进照片里。这样做的好处是,一次性记录了所有数据,一个都不少。

    缺点随着Redis中的数据变多,快照文件写入磁盘的时间也就越长。那么Redis在写入磁盘的时候可能会阻塞主线程,从而影响Redis的性能了。因此Redis提供了savebgsave两个命令来生产全量的RDB文件 。

    • save

      在主线程中执行,会导致阻塞;

    • bgsave

      创建一个子进程,专门用于写入 RDB 文件,避免了主线程的阻塞,这也是 Redis RDB 文件生成的默认配置。由于不会阻塞主线程,因此一般我们选择bgsave方式,他的具体实现逻辑如下:
      在这里插入图片描述
      如上图所示,快照开始时,主线程会fork出一个用于快照操作的子线程,并且复制一份数据对应的映射页表给子线程。子线程可以通过这个页表访问主线程的原始数据,然后将数据生成快照文件,存储到磁盘中。我们知道存储磁盘的时间是比较长的,当这个时候有请求进行想写数据怎么办呢?

      这个时候就要用到 写时复制,即当请求需要对键值C进行操作时,主线程会把新数据或修改后的数据写到一个新的物理内存地址上(键值对C’),并修改主线程自己的页表映射。所以,子进程读到的类似于原始数据的一个副本,而主线程也可以正常进行修改。
      这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。

  • 增量快照

    如果一直使用全量同步,一方面时间的推移,磁盘存储的快照文件会越来越多。另一方面如果频繁的进行全量同步,则需要主线线程频繁的fork出bgsvae线程,这样对Redis的性能是会产生影响的,并且也需要持续的对磁盘进行写操作。

    这个时候,我们可以采用另一只同步方式:增量快照。所谓增量快照,就是指,做了一次全量快照后,后续的快照只对修改的数据进行快照记录,这样可以避免每次全量快照的开销。在第一次做完全量快照后,T1 和 T2 时刻如果再做快照,我们只需要将被修改的数据写入快照文件就行。但是,这么做的前提是,我们需要记住哪些数据被修改了。你可不要小瞧这个“记住”功能,它需要我们使用额外的元数据信息去记录哪些数据被修改了,这会带来额外的空间开销问题。如下图所示:
    在这里插入图片描述

    如果我们对每一个键值对的修改,都做个记录,那么,如果有 1 万个被修改的键值对,我们就需要有 1 万条额外的记录。而且,有的时候,键值对非常小,比如只有 32 字节,而记录它被修改的元数据信息,可能就需要 8 字节,这样的画,为了“记住”修改,引入的额外空间开销比较大。这对于内存资源宝贵的 Redis 来说,有些得不偿失。

    所以说,全量快照和增量快照都有各自的优点和缺点,至于实际应用时,则要根据具体情况进行权衡。

AOF (Append Only File)

AOF(Append Only File) 持久化功能则提供了一种更为可靠的持久化方式。 每当 Redis 接受到会修改数据集的命令时,就会把命令追加到 AOF 文件的末尾,当你重启 Redis 时,AOF 文件里的命令会被重新执行一次,重建数据。

AOF持久化默认是关闭的,通过将 redis.conf 中将appendonly no,修改为appendonly yes来开启AOF 持久化功能,如果服务器开始了 AOF 持久化功能,服务器会优先使用 AOF 文件来还原数据库状态。只有在 AOF 持久化功能处于关闭状态时,服务器才会使用 RDB 文件来还原数据库状态。

当 AOF 持久化功能处于打开状态时,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的 aof_buf 缓冲区的末尾。

Redis 的服务器进程就是一个事件循环(loop),这个循环中的文件事件负责接受客户端的命令请求,以及向客户端发送命令回复,而时间事件则负责执行像 serverCron 函数这样需要定时运行的函数。

因为服务器在处理文件事件时可能会执行写命令,使得一些内容被追加到 aof_buf 缓冲区里面,所以在服务器每次结束一个事件循环之前,它都会调用 flushAppendOnlyFile 函数,考虑是否需要将 aof_buf 缓冲区的内容写入和同步到 AOF 文件里,这个过程可以用伪代码表示:

def evenLoop () :
    while True :
        processFileEvents() # 处理文件事件,接受命令请求以及发送命令回复
        processTimeEvents() # 处理时间事件,处理请求时可能会有新内容被追加到 aof_buf 缓冲区
        flushAppendOnlyFile() # 考虑是否要将 aof_buf 中的内容写入和保存到 AOF 文件里

flushAppendOnlyFile 函数的行为由服务器配置的appendfsync 选项的值来决定,因此我们需要设置同步选项,确保写命令同步到磁盘文件上的时机。有以下同步选项:

  • always:每个写命令都同步

    选项会严重减低服务器的性能;
    随着服务器写请求的增多,AOF 文件会越来越大。Redis 提供了一种将 AOF 重写的特性,能够去除 AOF 文件中的冗余写命令。

    当 appendfsync 的值为 always 时,服务器在每个事件循环都要将 aof_buf 缓冲区中的所有内容写入到 AOF 文件,并且同步 AOF 文件,所以 always 的效率是 appendfsync 选项三个值当中最慢的一个,但从安全性来说,always 也是最安全的,因为即使出现故障停机,AOF 持久化也只会丢失一个事件循环中所产生的命令数据。

  • everysec(默认):每秒同步一次,可以保证系统崩溃时只会丢失一秒左右的数据,并且 Redis 每秒执行一次同步对服务器性能几乎没有任何影响;

    当 appendfsync 的值为 everysec 时,服务器在每个事件循环都要将 aof_buf 缓冲区中的所有内容写入到 AOF 文件,并且每隔一秒就要在子线程中对 AOF 文件进行一次同步。从效率上来讲,everysec 模式足够快,并且就算出现故障停机,数据库也只丢失一秒钟的命令数据。

  • no: 让操作系统来决定何时同步,该选项并不能给服务器性能带来多大的提升,而且也会增加系统崩溃时数据丢失的数量

    当 appendfsync 的值为 no 时,服务器在每个事件循环都要将 aof_buf 缓冲区中的所有内容写入到 AOF 文件,至于何时对 AOF 文件进行同步,则由操作系统控制。因为处于 no 模式下的 flushAppendOnlyFile 调用无须执行同步操作,所以该模式下的 AOF 文件写入速度总是最快的,不过因为这种模式会在系统缓存中积累一段时间的写入数据,所以该模式的单次同步时长通常是三种模式中时间最长的。从平摊操作的角度来看,no 模式和 everysec 模式的效率类似,当出现故障停机时,使用 no 模式的服务器将丢失上次同步 AOF 文件之后的所有写命令数据。

AOF重写

随着服务器写请求的增多,AOF 文件会越来越大。Redis 提供了一种将 AOF 重写的特性,能够去除 AOF 文件中的冗余写命令。原理是首先从数据库中读取键现在的值,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令。

通过该功能,Redis 服务器可以创建一个新的 AOF 文件,新旧两个 AOF 文件所保存的数据库状态相同,但新 AOF 文件不会包含任何浪费空间的冗余命令,所以新 AOF 文件的体积通常会比旧 AOF 文件的体积小很多。

因为 aof_rewrite 函数生成的新 AOF 文件只包含还原当前数据库状态所必须的命令,所以新 AOF 文件不会浪费任何硬盘空间。

AOF后台重写

上面介绍的 AOF 重写程序 aof_rewrite 函数可以很好的创建一个新 AOF 文件的任务,但是因为这个函数会进行大量的写入操作,所以调用这个函数的线程将被长时间阻塞,服务器将无法处理客户端发来的命令请求。所以 Redis 决定将 AOF 重写程序放到子进程里执行,这样做可以同时达到两个目的:

  • 子进程进行 AOF 重写期间,服务器(父进程)可以继续处理命令请求。
  • 子进程带有服务器进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性。

但是,使用子进程也有一个问题需要解决,因为子进程在进行 AOF 重写期间,服务器进程还需要继续处理命令请求,而新的命令可能会对现有的数据库状态进行修改,从而使得服务器当前的数据库状态和重写后的 AOF 文件所保存的数据库状态不一致。

为了解决数据不一致问题,Redis 服务器设置了一个 AOF 重写缓冲区,这个缓冲区在服务器创建子进程之后开始使用,当 Redis 服务器执行完一个写命令之后,它会同时将这个写命令发送给 AOF 缓冲区和 AOF 重写缓冲区,如图所示。
在这里插入图片描述

这样一来可以保证:

AOF 缓冲区的内容会定期被写入和同步到 AOF 文件,对现有 AOF 文件的处理工作会如常进行。

从创建子进程开始,服务器执行的所有写命令都会被记录到 AOF 重写缓冲区里面。

当子进程完成 AOF 重写工作之后,它会向父进程发送一个信号,父进程在接到该信号后,会调用一个信号处理函数,并执行一下工作:

将 AOF 重写缓冲区中的所有内容写入到新 AOF 文件中,这是新的 AOF 文件所保存的数据库状态将和服务器当前的数据库状态一致。

对新的 AOF 文件进行改名,原子地(atomic)覆盖现有的 AOF 文件,完成新旧两个 AOF 文件的替换。

这个信号处理函数执行完毕之后,父进程就可以继续像往常一样接受命令请求了。

在整个 AOF 后台重写过程中,只有信号处理函数执行时会对服务器进程(父进程)造成阻塞,在其他时候,AOF 后台重写不会阻塞父进程,这将 AOF 重写对服务器性能造成的影响降到了最低。

以上就是 AOF 后台重写,也即是 BGREWRITEAOF命令的实现原理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值