redis学习(三) 数据安全和持久化
RDB快照
RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据快照。
触发机制:
手动触发 bgsave , save
save命令会阻塞redis服务器,直到快照建立完成。bgsave命令,redis进程会执行fork操作,建立子进程持久化,阻塞阶段只发生在fork阶段。
fork节点根据虚拟机和系统的不同,阻塞的时间不一致。
注意:Redis内部所有的涉及RDB的操作都采用bgsave的方式,而save命令已经废弃
自动触发:
-
使用save相关配置,即
save m n
表示在m秒内修改n次,自动触发 -
如果从节点执行全量复制操作,主节点自动执行bgsave操作
-
执行debug reload操作重新加载redis
-
默认请求下执行shutdown命令,因为默认参数是save,没有开启AOF
fork操作过程中阻塞父线程, info stats
命令查看lastest_fork_usec 选项查看最近一个fork操作的耗时,单位微秒。
lastsave命令查看最后一次生产RDB文件的时间,对应info统计的rdb_last_save_time选项。
RDB文件的保存目录:
dir 选项,rdb和aof文件的保存目录
当遇到坏盘或磁盘写满等情况时,可以通过config set dir{newDir}
在线修改文件路径到可用的磁盘路径,之后执行bgsave进行磁盘切换,同样适用于AOF持久化文件。
压缩:Redis默认采用LZF算法对生成的RDB文件做压缩处理,压缩后的文件远远小于内存大小,默认开启,可以通过参数config set rdbcompression{yes|no}
动态修改。
AOF
AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。
AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式。
appendonly yes,默认不开启。
AOF文件名通过appendfilename配置设置,默认文件名是appendonly.aof
appendfsync 文件同步选项对同步频率的影响,always | everysec 默认| no
AOF为什么把命令追加到aof_buf中?
Redis使用单线程响应命令,如果每次写AOF文件命令都直接追加到硬盘,那么性能完全取决于当前硬盘负载。先写入缓冲区aof_buf中,还有另一个好处,Redis可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。
重写机制
AOF文件重写是把Redis进程内的数据转化为写命令同步到新AOF文件的过程。
重写后的AOF文件为什么可以变小?有如下原因:
- 进程内已经超时的数据不再写入文件。
- 旧的AOF文件含有无效命令,如del key1、hdel key2、srem keys、seta111、set a222等。重写使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令。
- 多条写命令可以合并为一个,如:lpush list a、lpush list b、lpush listc可以转化为:lpush list a b c。为了防止单条命令过大造成客户端缓冲区溢出,对于list、set、hash、zset等类型操作,以64个元素为界拆分为多条。
AOF重写降低了文件占用空间,除此之外,另一个目的是:更小的AOF文件可以更快地被Redis加载。
AOF重写过程可以手动触发和自动触发:
- 手动触发:直接调用bgrewriteaof命令。
- 自动触发:根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机
auto-aof-rewrite-min-size:表示运行AOF重写时文件最小体积,默认为64MB。
auto-aof-rewrite-percentage:代表当前AOF文件空间aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的比值。
自动触发时机=aof_current_size>auto-aof-rewrite-min-size&&(aof_current_sizeaof_base_size)/aof_base_size>=auto-aof-rewrite-percentage
由于fork操作运用写时复制技术,子进程只能共享fork操作时的内存数据。由于父进程依然响应命令,Redis使用“AOF重写缓冲区”保存这部分新数据,防止新AOF文件生成期间丢失这部分数据。
子进程根据内存快照,按照命令合并规则写入到新的AOF文件。每次批量写入硬盘数据量由配置aof-rewrite-incremental-fsync控制,默认为32MB,防止单次刷盘数据过多造成硬盘阻塞。
文件加载流程:
RDB | AOF | |
---|---|---|
优点 | 非常适用于备份,全量复制等场景。比如每6小时执行bgsave备份,并把RDB文件拷贝到远程机器或者文件系统中(如hdfs),用于灾难恢复 | 适用于实时复制,增量复制 |
Redis加载RDB恢复数据远远快于AOF的方式 | 可以做到秒级别的复制 | |
不同版本会兼容 | ||
缺点 | RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高。 | |
老版本Redis服务无法兼容新版RDB格式的问题 |
文件修复
如果RDB和AOF文件损坏,可以使用官方提供的修复工具进行修复,但是修复之前记得保存一份原文件的备份
如aof文件修复
redis-check-aof --fix命令进行修复,修复后使用diff-u对比数据的差异,找出丢失的数据,人工修补。
如果断电导致aof尾部文件写入不全,redis提供了aof-load-truncated配置来兼容这种情况,默认开启。
持久化性能
fork操作
fork创建的子进程不需要拷贝父进程的物理内存空间,但是会复制父进程的空间内存页表。
例如对于10GB的Redis进程,需要复制大约20MB的内存页表,因此fork操作耗时跟进程总内存量息息相关,如果使用虚拟化技术,特别是Xen虚拟机,fork操作会更耗时。
正常情况下fork耗时应该是每GB消耗20毫秒左右。
改善:
不使用XEN虚拟机。
控制Redis实例最大可用内存,fork耗时跟内存量成正比,线上建议每个Redis实例内存控制在10GB以内。
合理配置Linux内存分配策略,避免物理内存不足导致fork失败。
降低fork操作的频率,放宽aof自动触发的时机,避免不必要的全量复制。
子进程开销监控
子进程负责AOF或者RDB文件的重写,它的运行过程主要涉及CPU、内存、硬盘三部分的消耗。
要点子进程工作的期间最好要保证同一个时间内只有一个子进程工作,因为子进程工作属于CPU密集型工作。
AOF重写时会消耗大量硬盘IO,可以开启配置no-appendfsync-on-rewrite,默认关闭。表示在AOF重写期间不做fsync操作。
在多服务器部署redis时,由于redis是内存实时运行的,不要在服务器上部署其他长时间占据内存和CPU的服务。
AOF追加阻塞
当开启AOF持久化时,常用的同步硬盘的策略是everysec,用于平衡性能和数据安全性。对于这种方式,Redis使用另一条线程每秒执行fsync同步硬盘。当系统硬盘资源繁忙时,会造成Redis主线程阻塞。
总结:fork阻塞时间跟内存量和系统有关,AOF追加阻塞说明硬盘资源紧张。
多实例部署
Redis单线程架构导致无法充分利用CPU多核特性,通常的做法是在一台机器上部署多个Redis实例。
当多个实例开启AOF重写后,彼此之间会产生对CPU和IO的竞争。
对于单机多Redis部署,如果同一时刻运行多个子进程,对当前系统影响将非常明显,因此需要采用一种措施,把子进程工作进行隔离。Redis在info Persistence中为我们提供了监控子进程运行状况的度量指标。
基于以上指标,可以通过外部程序轮询控制AOF重写操作的执行。
- 外部程序定时轮询监控机器(machine)上所有Redis实例。
- 对于开启AOF的实例,查看(aof_current_size-aof_base_size)/aof_base_size确认增长率。
- 当增长率超过特定阈值(如100%),执行bgrewriteaof命令手动触发当前实例的AOF重写。
- 运行期间循环检查aof_rewrite_in_progress和aof_current_rewrite_time_sec指标,直到AOF重写结束。
- 确认实例AOF重写完成后,再检查其他实例并重复2)~4)步操作。从而保证机器内每个Redis实例AOF重写串行化执行。