7.4 持久化

Redis持久化的两种方式

  • 全量模式
  • 增量模式

7.4.1 基于全量模式的持久化

将所有db的key-value完全保存下来,形成一个snapshot。当Redis重启时,加载snapshot数据,恢复至最近一次持久化时的状态。

1.写入流程

全量写入包含两种方式

  • SAVE
  • BGSAVE

SAVE可以由客户端显式触发,也可以在redis shutdown时触发。

SAVE以普通命令方式执行---单线程串行化地执行一个一个命令。

SAVE执行过程就是把Redis的当前状态写入磁盘作为快照保存的过程,期间其他所有命令不会并发执行,所以即便写入磁盘过程持续时间很长,数据的状态始终是一致的,不会发生变更。

BGSAVE可以有客户端通过命令显式触发,也可以通过配置定时任务触发,也可以在master-slave的分布式结构下由slave节点触发(后文)

BGSAVE命令执行开始于fork一个子进程,有这个子进程将Redis数据写入磁盘,这个子进程写入操作可能持续数秒到几十分钟不等,期间不影响Redis对外服务的可用性。

BGSAVE生成的快照的数据状态是fork子进程时Redis的数据状态,父进程一旦完成fork,后续执行的新的客户端命令对数据状态产生的变更不会反应在本次快照文件中。子进程写入文件面对的是父进程在fork时的数据库状态副本,该副本在整个磁盘写入期间不会发生变更。

BGSAVE相对于SAVE的优势是持久化期间可以持续提供数据读写服务,作为代价,子进程fork时,涉及父进程内存的复制,其存在期间会增加服务器内存开销,内存开销高到不得不使用虚拟内存时,BGSAVE的fork会阻塞服务器运行,造成秒级以上的不可用。因此,使用BGSAVE需保证Redis服务器空闲时间内存足够。

2.恢复流程

从Redis启动到进入前文所述事件处理主循环时,Redis会从本地磁盘加载之前持久化的文件,将内存置于文件所描述的数据“状态”时,在受理后续来自客户端的数据访问命令。

7.4.2 基于增量模式的持久化

全量持久化保存的是数据的“状态”,而增量持久化保存的则是状态的每一次“变迁”。

Redis中,增量持久化称为AOF(append-only file)方式,在此基础上rewrite机制优化性能。

1.写入流程

Redis增量持久化在主循环中的每次处理完写命令的执行之后,通过propagate函数触发,如图:

 propagate方法将当前命令的内容append到redisServer对象的aof_buf变量中。主循环在下一个迭代进入多路复用的select方法前,Redis会通过flushAppendOnlyFile方法将aof_buf的内容write到AOF对应的文件中。但write操作只是将数据写到缓存,什么时候从缓存真正落地道磁盘,取决于操作系统。只有显式调用fsync()方法才能强制地让操作系统落地数据到磁盘。

Redis的AOF包含3中同步策略:

  • always:主循环的每个迭代的flushAppendOnlyFile函数中直接同步触发fsync方法,强制数据落地磁盘。该策略会降低Redis吞吐量,使得磁盘的写入成为Redis对外写服务的瓶颈,但由于每个命令都在写入磁盘后才返回,这种模式有最高的容错能力。
  • every second:每秒异步地触发一次fsync方法。fsync方法的执行者是bio线程池中的某线程。flushAppendOnlyFile函数只是作为生产者将fsync任务放入队列,由bio线程消费执行。
  • no:不显示调用fsync,有操作系统决定什么时候落地磁盘。这种模式下,Redis无法决定增量的落地时间,因此容错能力不可控。

对于every second策略,Redis实际的吞吐量仍和磁盘写入能力相关,只是1s内的请求会批量一次性写入磁盘提升写入吞吐量,但Redis的对外写服务吞吐量仍然不可能超过磁盘的写入吞吐量,否则造成bio任务队列挤压,通常为保护内存用量会限制任务队列的长度使得后续提交任务时阻塞。redis仍然通过阻塞来迟了磁盘吞吐量过低的情况,但阻塞不会发生在任务队列上。Redis发现bio执行fsync的线程还在执行中的时候,不会再往队列提交任务,阻塞发生在write函数(写入缓存的函数)上:当bio线程执行fsync时,write方法自然会阻止。

7.4.3 基于增量模式持久化的优化

大量的新的数据append到AOF文件中,占用大量磁盘空间,同时降低Redis启动时回放加载效率。Redis通过rewrite机制合并历史AOF记录,如图:

Redis的这份快照仍然使用cmd形式来承载(???),只是将快照的所有key-value值用插入命令来表示。这样一来,rewrite出来的快照文件和普通的AOF文件格式一致,可复用相同的加载逻辑统一处理Redis启动时的数据恢复。

rewrite通过bgrewrite方法实现,如图:

主循环运行到定时任务处理时,Redis发现Rewrite条件满足,则通过rewriteAppendOnlyFileBackground函数fork出一个子进程

子进程创建完成后获得Redis主进程的数据状态,子进程将状态写入rewrite的AOF文件中。

子进程运行期间,Redis主进程继续对外提供服务,新的增量写入redisServer对象的aof_rewrite_buf_blocks中,待子进程完成后,这部分内容将append到rewrite快照文件末尾,在后续的增量,会写入新的AOF文件。

整个过程中持久化的数据如下:

历史AOF:以快照的方式(仍然使用cmd形式,但转换成插入命令)保存。

快照写入期间的增量:待快照写入完成后append到快照文件末尾。

再后续的增量:写入新的AOF。

 

 

优缺点

  • RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据备份。非常适合备份,全量复制等场景。比如每6小时执行 bgsave 备份,并把 RDB 文件拷贝到远程机器或者文件系统中,用于灾难恢复。
  • Redis 加载 RDB 恢复数据远远快于 AOF 的方式
  • RDB 方式数据没办法做到实时持久化,而 AOF 方式可以做到。

 

 

链接

 

posted @ 2020-05-04 18:24  vvf  阅读(156)  评论(0编辑  收藏  举报