Redis高可用—数据持久化

Redis的高可用从以下几个方面来保证:

  • 数据持久化
  • 主从复制
  • 自动故障恢复
  • 集群

笔者将对以上几个方面进行逐一介绍,今天这篇文章先来介绍Redis的数据持久化。

一、为什么需要持久化?

Redis是内存数据库,数据全部保存在内存中,如果实例宕机,数据将会全部丢失,Redis提供了完善的数据持久化机制,可以把数据持久化到磁盘上,方便我们进行备份和快速恢复数据。

二、Redis数据持久化方式

Redis提供了2种数据持久化方式:

  1. RDB:将内存中的数据以快照的形式保存到硬盘里,生成dump.rdb文件,该文件是一个二进制文件。因为RDB文件保存在磁盘里,即使Redis服务器进程退出,RDB文件还在,Redis服务器在启动时检测到RDB文件存在,会自动载入RDB文件,用它来还原数据库状态。
  2. AOF:AOF持久化通过保存Redis服务器执行的写命令记录数据库状态,AOF文件保存了Redis写命令请求协议,是纯文本格式,可以直接打开AOF文件查看文件内容。

注意:
1. Redis默认开启RDB持久化功能;
2. AOF默认关闭,如果手动开启了AOF持久化功能,会优先使用AOF文件来还原数据库状态,因为AOF文件的更新频率通常比RDB文件的更新频率高。
Redis启动数据加载流程

1. RDB持久化

1.1 什么时候触发RDB持久化?

触发RDB持久化的情况分为:手动触发和自动触发。

1.1.1 手动触发

手动触发命令:

  1. save:阻塞式,内存较大的实例在执行过程中会造成长时间的阻塞,期间主进程不能处理任何命令请求。
  2. bgsave:fork子进程,由子进程负责RDB文件的创建,父进程可以继续处理命令的请求。阻塞发生在fork阶段,时间较短。
1.1.2 自动触发

自动触发RDB的条件有:

  1. 使用save配置;
    配置格式:save 【seconds】 【changes】
    表示【seconds】秒内数据集存在【changes】次修改时,自动触发bgsave。
    默认配置为:
#只要以下3个条件中的任意一个满足,bgsave就会执行。
#服务器在900秒内,对数据库进行了至少1次修改
save 900 1
#服务器在300秒内,对数据库进行了至少10次修改
save 300 10
#服务器在60秒内,对数据库进行了至少10000次修改
save 60 10000

实现原理:

#serverCron服务定时器每100ms执行一次检查
if(now()-rdb_last_save_time < m(指定秒数) and rdb_changes_since_last_save>n(修改次数)){
    
    
   bgsave();
}
  1. 从节点执行全量复制操作;
  2. 执行shutdown命令时,如果没有开启AOF自动执行bgsave;
  3. debug reload命令;

注意如果开启了自动RDB,flushall因涉及的操作较多,可能会触发自动RDB,新产生的RDB文件将为空。

1.2 redis.conf 中RDB相关配置
#RDB自动触发条件(满足任意一个就可以触发RDB)
save 900 1
save 300 10
save 60 10000
#bgsave创建快照的时候出错了(比如,fork子进程内存不足或rdb文件所在的文件夹没有写入权限),redis主进程将不再接受新的写入命令
stop-writes-on-bgsave-error yes
#对RDB文件进行压缩
rdbcompression yes 
#RDB文件名称
dbfilename dump.rdb
#对RDB进行校验
rdbchecksum yes
 #(保存目录,这里是相对目录,和redis.conf是同一个目录)
dir ./ 

1.3 RDB持久化步骤
  1. 执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进程,如RDB/AOF子进程,如果存在bgsave命令直接返回;
  2. 父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞,通过info stats命令查看latest_fork_usec选项,可以获取最近一个fork操作的耗时,单位为微秒;
  3. 父进程fork完成后,bgsave命令返回“Background saving started”信息并不再阻塞父进程,可以继续响应其他命令;
  4. 子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。执行lastsave命令可以获取最后一次生成RDB的时间,对应info统计的rdb_last_save_time选项。
  5. 进程发送信号给父进程表示完成,父进程更新统计信息,具体见info Persistence下的rdb_*相关选项。

注意fork子进程时,需要生成另外一份内存,如果原来内存占用比较大,会存在内存膨胀的问题。
对于错误格式的RDB文件,可以使用redis-check-rdb 工具进行修复。

2. AOF持久化

2.1 redis.conf 中AOF相关配置
#开启AOF,默认关闭
appendonly yes
#AOF文件名称
appendfilename appendonly.aof
 #(保存目录,这里是相对目录,和redis.conf是同一个目录)
dir ./ 

#AOF文件写入磁盘频率
appendfsync always  #每次收到写命令就立即强制写入磁盘,是最安全的。但该模式下速度也是最慢的,一般不推荐使用。
appendfsync everysec #默认方式,每秒钟强制写入磁盘一次,在性能和持久化方面做平衡,推荐该方式。
appendfsync no  #完全依赖操作系统的写入,一般为30秒左右一次,性能最好但是持久化最没有保证,不推荐。

/*AOF重写相关命令*/
#在日志重写时,不进行命令追加操作,而只是将其放在缓冲区里,避免与命令的追加造成DISK IO上的冲突。设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes
no-appendfsync-on-rewrite yes
#AOF重写触发条件
auto-aof-rewrite-percentage 100 #当前AOF文件大小是上次日志重写得到AOF文件大小的二倍时,自动启动新的日志重写过程。
auto-aof-rewrite-min-size 64mb #当前AOF文件启动新的日志重写过程的最小值,避免刚刚启动Reids时由于文件尺寸较小导致频繁的重写。
2.2 AOF持久化步骤

AOF持久化的实现可以分为:命令追加、文件写入、文件同步3个步骤。

  1. 命名追加:服务器在执行完一个写命令后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾;
  2. 文件写入和同步:服务器定时会检查是否需要将aof_buf缓存区中的内容写入和保存到AOF文件中。是否需要文件写入是根据appendfsync 选项设置的,可以从AOF持久化的效率和安全性出发,设置appendfsync 选项的值。

注意:对于错误格式的AOF文件,先进行备份,然后采用redis-check-aof --fix命令进行修复,修复后使用diff-u对比数据的差异,找出丢失的数据,有些可以人工修改补全。

2.3 AOF重写
2.3.1 为什么AOF重写?

写命令追加到AOF文件,AOF会越来越大,占用过多的硬盘空间,并且重启之后的数据加载也会很慢,因此需要AOF重写。

2.3.2 AOF重写触发机制

触发AOF重写的方式分为:手动触发、自动触发。

  1. 手动触发:手动执行命令BGREWRITEAOF;

  2. 自动触发:通过配置redis.conf中auto-aof-rewrite-percentageauto-aof-rewrite-min-size来实现,这两个条件同时满足时触发AOF。

2.3.3 AOF重写规则

AOF重写并不会对现有AOF文件进行读取和分析操作,它是通过读取服务器当前数据库状态来实现的。
因此过期的数据不会写入新AOF文件,之前针对同一个key的多个命令也会因为直接读取数据库状态由一个命令代替。并且,为避免一个key包含的数据过多导致客户端输入缓冲区溢出,重写程序在处理列表、集合、有序集合、哈希等可能包含多个元素的键时,会先判断键所包含的元素数量,如果超过配置的常量值(默认是64)了,会拆分为多个命令。

2.3.4 AOF重写步骤
  1. 执行AOF重写请求。如果当前进程正在执行AOF重写则不执行重写操作,如果进程在执行bgsave则等待执行完毕后再执行。
  2. 父进程执行fork创建子进程,开销等同于bgsave过程。
  3. (1)主进程fork操作完成后,继续响应其他命令。所有修改命令依然写入AOF缓冲区并根据appendfsync策略同步到硬盘,保证原有AOF机制正确性。
    (2)由于fork操作运用写时复制技术,子进程只能共享fork操作时的内存数据。由于父进程依然响应命令,Redis使用“AOF重写缓冲区”保存这部分新数据,防止新AOF文件生成期间丢失这部分数据。
  4. 子进程根据内存快照,按照命令合并规则写入到新的AOF文件。每次批量写入硬盘数据量由配置aof-rewrite-incremental-fsync控制,默认为32MB,防止单次刷盘数据过多造成硬盘阻塞。
  5. (1)新AOF文件写入完成后,子进程发送信号给父进程,父进程更新统计信息,具体见info persistence下的aof_*相关统计。
    (2)父进程把AOF重写缓冲区的数据写入到新的AOF文件。
    (3)使用新AOF文件替换老文件,完成AOF重写。

3. RDB和AOF对比

  1. RDB性能高于AOF:
    ● 采用二进制方式存储数据,数据文件比较小,加载快速;
    ● 存储的时候是按照配置中的save策略来存储,每次都是聚合很多数据批量存储,写入的效率很好,而AOF则一般都是工作在实时存储或者准实时模式下,相对来说存储的频率高,效率却偏低。
  2. AOF数据安全性高于RDB:
    ● 存储是基于累计批量的思想,也就是说在允许的情况下,累计的数据越多那么写入效率也就越高,但数据的累计是靠时间的积累完成的,那么如果在长时间数据不写入RDB,但Redis又遇到了崩溃,那么没有写入的数据就无法恢复了,但是AOF方式偏偏相反,根据AOF配置的存储频率的策略可以做到最少的数据丢失和较高的数据恢复能力。
    二者选择的标准,就是性能和数据安全性之间的一个权衡。

三、参考文献

  1. https://zhuanlan.zhihu.com/p/111306444
  2. 《Redis设计与实现》

猜你喜欢

转载自blog.csdn.net/zpy20120201/article/details/109301628