Redis支持2种持久化机制:RDB 和 AOF。持久化机制可以有效避免因进程退出造成的数据丢失问题。当下次重启的时候利用之前持久化的文件即可实现数据恢复。
一 RDB
RDB持久化是把当前进程数据生成快照保存到硬盘的过程,触发RDB持久化过程分为手动触发和自动触发。
1.1 触发机制
1.1.1手动触发:save |bgsave
save: 阻塞当前redis服务器,直到RDB过程完成为止,对于内存比较大的实例会造成长时间阻塞,线上环境不建议使用
bgsave: Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束,阻塞只发生在fork阶段,一半时间很短。
1.1.2 自动触发
# 使用save m n 配置:表示m秒内数据集存在n次修改时自动触发bgsave操作
# 如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点
# 默认情况下,shutdown的时候,如果没有开启AOF持久化功能,则自动执行bgsave
1.2 触发流程
# 执行bgsave命令, Redis父进程判断当前是否存在正在执行的子进程,如RDB/AOF子进程,如果存在bgsave命令直接返回
# 父进程直接fork一个新的子进程,父进程继续响应其他请求
# fork出来的子进程开始生成RDB文件
# RDB文件创建之后,通知父进程
1.3RDB文件的处理
RDB文件默认保存在redis.conf配置文件中dir目录下,文件名通过dbfilename配置制定。可以通过执行config set dir {newdir} 和 config
set dbfilename {newFileName}运行期间动态执行,当下次运行时RDB就会保存到新目录。
当遇到坏磁盘或者磁盘写满的情况,可以通过config set dir {newdir} 在线修改文件路径到可用的磁盘,之后执行bgsave进行磁盘切换,同样适用于AOF持久化文件
Redis默认采用LZF算法对RDB文件进行处理,压缩后的文件远远先于内存,建议生产环境开启rdbcompression yes|no
1.4RDB的优缺点
优点:
# RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据快照。非常适用于备份,全量复制等场景。
# Redis加载RDB恢复数据远远快于AOF的方式
缺点:
# RDB方式没有办法做到实时持久化、秒级持久化。因为bgsave每次都要fork子进程,属于重量级操作,频繁执行成本过高
# RDB使用特定二进制格式保存,存在老板的Redis无法兼容新版本的RDB格式
二 AOF
AOF: Append Only File持久化,以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令达到恢复数据的目的。AOF主要解决了数据持久化的实时性,现在已经是主流方式。
AOF默认是没有打开的,需要在配置文件加上appendonly yes.
AOF文件名:appendfilename
存放路径也是在配置文件dir配置的目录下。
工作流程如下:
写入命令:append
文件同步:sync
文件重写:rewrite
重启加载:load
2.1 命令写入
就是采用文本协议格式,将命令写入文件
2.2 文件同步
Redis提供了集中AOF缓冲区同步文件策略。由参数appendfsync控制:
always: 命令写入aof_buf后调用系统fsync操作同步到AOF文件,fsync完成后返回。
每次写入都要同步AOF文件,在一般的SATA硬盘上,Redis只能支持大约几百TPS写入,显然不符合Redis高性能的气质,所以不建议配置
everysec: 命令写入aof_buf后调用系统write操作,write完成后返回,fsync同步文件由专门的线程每隔一秒调用一次
建议的同步策略。兼顾性能和数据安全,理论上宕机的情况下丢失1秒的数据。
no: 命令写入aof_buf后调用fsync调用write操作,不对AOF文件做fsync同步,同步操作由操作系统负责,一般而言,30秒同步一次
由于操作系统每次同步AOF文件的周期不可控,而且会加大每次同步数据量,虽然提升了性能,但是数据安全性无法保证
2.3 重写机制
随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入了AOF重写机制压缩文件体积。即把Redis进城内的数据转化为写命令同步到新AOF文件的过程。
体积变小的原因:
# 进城内已经超时的数据不再写入文件
# 多条写命令可以合并为一个,lpush list a,lpush list b可以合并为lpush list a b
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_current_size > auto-aof-rewrite-min-size && (aof_current_size - aof_base_size) / aof_base_size >=auto-aof-rewrite-percentage
bgrewriteaof触发的流程:
# 执行AOF重写请求,如果当前父进程正在重写,请求不会执行;如果当前进程正在执行bgsave操作,重写命令延迟到bgsave之后再执行
# 父进程fork一个子进程,等价于bgsave过程
# 主进程fork后,继续响应其他命令;所有修改命令依然写入AOF缓冲区并根据appendfsync策略同步到硬盘,保证原有AOF的机制的正确性
# 由于fork运用的copy-on-write技术,子进程只能获取fork时的内存数据,而对于fork之后父进程的继续处理的命令,则是使用AOF重写缓存区保存这部分新数据,防止新AOF文件生成期间丢失这部分数据
# 子进程根据内存快照,按照命令合并规则写入到新的AOF文件。每一次批量写入硬盘数据量由aof-rewrite-increment-fsyn控制,默认32M,防止单词刷新数据过多造成硬盘阻塞。
# 新AOF文件写好后,在子进程发送信号给父进程,父进程更新统计信息
# 父进程把AOF重写缓冲区数据写入到新的AOF文件
# 使用新的AOF替换老文件,完成AOF重写
2.4 重启加载
# 如果开启AOF,则判断是否有AOF文件
# 如果AOF文件存在,则加载AOF文件
# 如果AOF没有可以活着开启了但是没有AOF文件,检查是否有RDB文件
# 如果RDB文件不存在则启动成功,不需要加载任何文件
# 如果RDB文件存在则加载RDB文件
2.5 文件校验
如果加载了损坏的AOF文件,Redis会启动不了。对于错误格式的AOF文件,先进行备份,然后采用redis-check-aof –fix命令进行套修复。修复后使用diff -u 对比数据差异,找出丢失的数据
三 问题定位与优化
Redis的持久化功能,对Redis的性能影响比较大。
3.1 fork操作
fork操作是RDB或者AOF重写时,必须的一个步骤,创建一个子进程,我们知道fork是一个重量级操作,虽然fork创建子进程不需要拷贝父进程的物理内存空间,但是会复制父进程空间空间内存页表。对于10G的Redis进程,需要复制大约20M的内存页表,因此fork操作耗时跟进程总内存量有关系
fork耗时定位: 对于高流量的Redis实例,OPS可以达到5w以上,如果fork操作耗时在秒级别将拖慢Redis几万命令的执行,对线上的应用延迟有很大影响。正常情况下,fork耗时应该是每GB消耗20毫秒左右。可以在info stats统计中查lastest_fork_usec指标获取最近一次fork操作耗时,单位微秒。
如何改善fork操作的耗时:
# 控制Redis实例最大可用内存,fork耗时跟内存量成正比,线上建议每一个Redis实例内存控制在10GB以内
# 合理配置Linux内存分配策略,避免物理内存不足导致fork失败
# 降低fork操作频率,如适度放宽AOF自动触发时机
3.2 子进程开销监控和优化
子进程负责AOF或RDB文件重写和创建,它的运行过程主要涉及到CPU,内存,硬盘三部分的消耗
3.2.1CPU
# CPU开销分析: 子进程负责把进程内的数据分批写入文件,这个过程属于CPU密集操作,通常子进程对单核CPU的利用率接近90%。
# CPU消耗优化:Redis是CPU密集型服务,不要做绑定单核CPU的工作,由于子进程非常消耗CPU,回合父进程产生单核资源的竞争
3.2.2 内存
# 内存消耗分析: 子进程通过fork操作产生,占用内存大小等同于父进程,理论上需要2倍内存完成持久化操作,但Linux有copy-on-write机制,父子进程会共享相同的物理内存页,当父进程处理写请求的时候会把要修改的页创建副本,而子进程在fork操作中共享整个父进程内存快照
# 内存消耗监控
RDB重写时,Redis日主输出:
如果重写过程存在内存修改,父进程负责创建所修改的内存页的副本,从日志中可以看出是5MB.
父进程维护页副本消耗同RDB重写过程类似,不同之处在于AOF重写需要AOF重写缓冲区,因此根据以上日志可用预计
存消耗为53 + 1.49,也就是AOF重写子进程消耗的内存量
# 内存消耗优化
尽量避免多个实例同一时刻只有一个子进程在工作;
避免在大量写入时做子进程重写操作,这样将导致父进程维护大量页副本
3.2.3 硬盘
# 硬盘开销分析:子进程主要就是把AOF或者RDB写入磁盘持久化,势必给磁盘造成压力,可以集合sar,iostat,iotop等分析此期间硬盘负载情况如何
# 硬盘开销优化:
不要和其他高磁盘负载的服务部署在一起,比如存储服务,消息队列服务
AOF重写会消耗大量磁盘IO,可以开启no-appendfsync-on-rewrite默认是关闭的,在AOF重写期间不做fsync操作。极端情况下,可能会丢失整个AOF重写期间数据,需要根据安全性决定是否设置
对于单机配置多个实例,可以配置不同的实例分盘存储AOF文件,分摊硬盘写入压力
3.3 AOF追加阻塞
我们知道,开启AOF持久化机制的时候,推荐的持久化策略时everysec,可以兼容性能和安全。对于这种方式,同步的时候,会使用另外一个线程去做同步操作。当系统硬盘资源繁忙时候要,可能会阻塞Redis主线程。
阻塞流程:
# 主线程负责向AOF缓冲区写入命令
# AOF同步线程负责每一秒执行一次磁盘同步,并记录最近一次同步时间
# 主线程负责对比上次AOF同步时间:如果距上次同步成功在2秒内,主线程直接返回;如果距上次同步成功已经超过了2s,主线程将会阻塞,直到同步操作完成
AOF阻塞定位:
# Redis日志在发生阻塞的时候,会输出如下日志,用于记录AOF fsync阻塞导致拖慢Redis服务的行为
# 每当发生AOF追加阻塞事件发生时,在infoPersistence统计中,aof_delayed_fsync指标会累加
# AOF 同步最多允许2秒的延迟,当延迟发生的时候,说明硬盘存在高负载问题哦,可以通过监控工具如iotop定位消耗硬盘资源的进程。
四 多实例部署
Redis单线程架构导致无法充分利用CPU多核特性,通常做法在一台机器上部署多个Redis实例。当多个实例开启AOF以后,彼此会产生对CPU和IO的竞争。如果同时运行多个子进程,那么对系统影响非常大,所以把子进程工作隔离开来。
我们可以通过获取指标,使用外部程序轮询控制AOF重写操作,如下:
# 外部程序定时轮询监控机器上所有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重写完成后,在检查其他实例,从而保证每一个Redis实例AOF重写串行化执行。
五 RDB 和 AOF 比较
5.1 就文件大小而言,相同的数据集,RDB更加紧凑,体积比AOF更小;但是AOF体积到一定程度时,可以自动的进行AOF重写,重写后会对之前的过期,无效的等命令过滤掉,而且会对一些命令近些合并。
5.2 就恢复数据的时候,特别是大数据的时候,由于AOF需要一条命令一条命令的执行,所以RDB比AOF恢复速度更快
5.3 就备份而言,RDB更加适合备份容灾,因为体积小,比较方便传送
5.4 就数据安全性而言,AOF最多丢失2秒的数据,而RDB有可能丢失数分钟的数据,因为RDB是根据保存点来进行内存快照的,如果保存点设置的太频繁,那么fork的时候对系统影响很大。所以相对来说AOF比RDB更安全些
5.5 就文件可读性来说,AOF可读性好,因为它是基于Redis协议格式保存的,所以当错误用了某些命令,比如FLUSHALL,我们也可以打开AOF文件,删除末尾的FLUSHALL命令,重启Redis恢复到之前的状态
六 如何选择使用哪种持久化方式
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快, 除此之外, 使用 RDB 还可以避免之前提到的 AOF 程序的 bug 。
七 线上怎样从RDB方式切换为AOF方式
在 Redis 2.2 或以上版本,可以在不重启的情况下,从 RDB 切换到 AOF :
为最新的 dump.rdb 文件创建一个备份。
将备份放到一个安全的地方。
执行以下两条命令:
redis-cli config set appendonly yes
redis-cli config set save “”
确保写命令会被正确地追加到 AOF 文件的末尾。
执行的第一条命令开启了 AOF 功能: Redis 会阻塞直到初始 AOF 文件创建完成为止, 之后 Redis 会继续处理命令请求, 并开始将写入命令追加到 AOF 文件末尾。
执行的第二条命令用于关闭 RDB 功能。 这一步是可选的, 如果你愿意的话, 也可以同时使用 RDB 和 AOF 这两种持久化功能。
重要:别忘了在 redis.conf 中打开 AOF 功能! 否则的话, 服务器重启之后, 之前通过 CONFIG SET 设置的配置就会被遗忘, 程序会按原来的配置来启动服务器。