Redis持久化
- 首先是一张整理好的思维导图
Redis持久化有两种方式,rdb和aof:
- rdb会保存一个时间点的数据快照
- aof会记录每一个服务器收到的写操作。在服务器启动时,这些记录的操作会逐行从aof文件中执行,从而重建原先的数据集。写操作命令记录的格式与Redis协议一致,以追加的方式进行保存
- Redis持久化可以禁用
- Redis两种持久化方式可以同时存在,重启redis时,aof优先用于重建
RDB
工作原理
- redis调用fork(),产生一个子进程
- 子进程把数据写到临时的rdb文件中
- 当子进程写完新的rdb文件后,把旧的rdb文件替换掉
优点
- rdb快照是一个简洁的单文件,保存了某个时间点的redis数据集
- rdb适合做灾备,单个rdb文件能很方便的进行传输
- 性能好。因为进行持久化时,主进程会fork一个子进程出来,然后把持久化的工作交给子进程,自己不会有相关的IO操作
- 在数据量较大的情况下,rdb启动得更快
缺点
- 很容易造成数据的丢失,假设每5min保存一次,出现意外的话,我们可能丢失4min59s的数据
- rdb使用fork产生子进程进行数据的持久化,如果数据比较大的话可能会花费点时间,造成redis停止服务几毫秒,如果数据量很大且cpu性能不是很好的时候,停止服务的时间甚至会到1s
rdb的启用和禁用
使Redis如果在每N秒后数据发生了M次改变就自动保存快照文件。
# 格式为:save <seconds> <changes>
# 可以设置多个。
save 900 1 #900秒后至少1个key有变动
save 300 10 #300秒后至少10个key有变动
save 60 10000 #60秒后至少10000个key有变动
禁用快照保存功能
save ""
其他的自动触发rdb的方式
- 从节点全量复制时,主节点发送rdb给从节点完成复制操作,主节点会触发bgsave
- 执行debug reload时
- 执行shutdown时,如果没有开启aof,也会触发
如果rdb时发生错误
默认情况下,如果redis在后台rdb时失败,Redis则会停止接受更新操作,这样会让用户了解到数据没有被正确的存储到磁盘上。否则没人会注意到这个问题,可能会造成灾难。 如果后台存储(持久化)操作进程再次工作,Redis会自动允许更新操作。然而,如果你已经恰当的配置了对Redis服务器的监视和备份,你也许想关掉这项功能。如此一来即使后台保存操作出错,redis也仍然可以继续像平常一样工作。
stop-writes-on-bgsave-error yes
数据压缩配置
默认redis会采用lzf(一种压缩算法)对数据进行压缩,禁用掉可以节省cpu
rdbcompression yes
数据校验配置
从版本5的RDB的开始,一个CRC64
的校验码会放在文件的末尾。这样更能保证文件的完整性,但是在保存或者加载文件时会损失一定的性能(大概10%)。如果想追求更高的性能,可以把它禁用掉,这样文件在写入校验码时会用0
替代,加载的时候看到0
就会直接跳过校验。
rdbchecksum yes
手动生成快照
有两个命令
SAVE
SAVE会使用同步的方式生成RDB快照文件,这意味着在这个过程中会阻塞所有其他客户端的请求,直到持久化完成。因此不建议在生产环境使用,除非因为某种原因需要去阻止redis使用子进程进行后台生成快照。
BGSAVE
BGSAVE命令该触发方式会fork一个子进程,由子进程负责持久化过程,因此阻塞只会发生在fork子进程的时候。而我们可以使用LASTSAVE命令查看操作的结果。
127.0.0.1:6379> BGSAVE
Background saving started
127.0.0.1:6379> LASTSAVE
(integer) 1433936394
配置文件里禁用了快照生成功能不影响SAVE和BGSAVE命令的效果
BGSAVE的原理
这里注意的是fork操作会阻塞,导致Redis读写性能下降。我们可以控制单个Redis实例的最大内存,来尽可能降低Redis在fork时的时间消耗。以及上面提到的自动触发的频率减少fork次数,或者手动触发,根据自己的机制来完成持久化。
关于fork操作
同步操作 ,fork 是一个同步操作
- 当执行一个 bgsave 或 bgrewriteaof 首先会执行一个 fork 操作,它只是做一个内存页的拷贝,并不是拷贝所有内存,所以他的速度是非常快的。
- 当 fork操作比较慢或卡在某一个点,这时它会阻塞redis主线程
与内存量息息相关:内存越大,消耗越长(与机器类型有关)
info:latest_fork_usec – 可以查看 fork 的执行时间
改善fork
优先使用物理机或者高效支持fork操作的虚拟化技术
控制Redis 实例最大可用内存:maxmemory
合理配置Linux内存分配策略:vm.overcommit_memory = 1
默认是0,当发现没有足够内存做内存分配时,就会不去分配。对于fork讲,会造成fork阻塞。
降低fork频率:例如放宽AOF重写自动触发机制,不必要的全量复制
—— 常见的持久化开发运维问题
总之就是数据量越大fork卡顿的时间就会越长
AOF
由于rdb容易丢失最新的数据,所以aof提供了一种更可靠的持久化方式。每当redis接受到修改命令时,就会把命令追加到aof文件里去,当重启redis服务时,aof里的命令会被重新执行一次,重建数据。
工作过程
- 命令传播:Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF 程序中。
- 缓存追加:AOF 程序根据接收到的命令数据,将命令转换为网络通讯协议的格式,然后将协议内容追加到服务器的 AOF 缓存中。
- 文件写入和保存:AOF 缓存中的内容被写入到 AOF 文件末尾。如果设定的 AOF 保存条件被满足的话,
fsync
函数或者fdatasync
函数会被调用,将写入的内容真正地保存到磁盘中。
优点
- 更可靠。有三种模式不进行fsync(
fsync是指把缓存中的写指令记录到磁盘中
) ,每秒一次fsync,每次修改进行fsync。一般用每秒一次的,意味着你最多丢失一秒钟的数据。 - aof日志文件是一个纯追加的文件。不会出现日志损坏问题,海域redis-check-aof可以进行修复文件
- aof文件太大时,redis支持后台自动重写。重写是在一个新的文件上进行,同时redis继续往旧的aof文件中追加数据。新文件上会写入能重建当前数据集的最小操作命令集合。重写完毕后,redis会将新的aof文件替换掉旧的aof,然后把数据开始写到新文件上。
- AOF把操作命令以简单易懂的格式一条接一条的保存在文件里,很容易导出来用于恢复数据。例如我们不小心用
FLUSHALL
命令把所有数据刷掉了,只要文件没有被重写,我们可以把服务停掉,把最后那条命令删掉,然后重启服务,这样就能把被刷掉的数据恢复回来。
缺点
- aof文件较大于rdb
- 每秒一次fsync,每次修改进行fsync策略下,速度慢于rdb
相关的配置
appendony yes ##启用aof
dir ./ ##文件存放目录,与rdb共用
appendfilename ##默认文件名
## fsync策略(三种)
appendfsync always ## 每当有新命令追加到AOF的时候调用fsync。速度最慢,但是最安全。
appendfsync everysec ## 每秒fsync一次。速度快(2.4版本跟快照方式速度差不多),安全性不错(最多丢失1秒的数据)。
appendfsync no ## 从不fsync,交由系统去处理。这个方式速度最快,但是安全性一般。
关于appendfsync no的说明
在这种模式下,在写入内存的aof_buf后,redis仅仅将aof_buf写入到aof文件中,但不会调用fsync
函数或者 fdatasync
函数将写入的内容真正地保存到磁盘中。
在这种模式下,真正的保存只会在一下任意一种情况中被执行:
- redis被关闭
- aof功能被关闭
- 系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行)
这三种情况都会引起redis主进程阻塞,always的保存功能是由redis主进程执行的,所以也会阻塞。everysec中,保存功能是由子进程进行的,所以不会引起主进程阻塞。
其日志重写原理
当写操作命令不断增加时,aof文件会不断增大,有一种aof文件优化思路是“只需要重建能够恢复当前状态的aof文件即可,也就是说如果有一个递增的计数器到100次,新的aof文件只需要记录其最后的值即可”。
重写流程如下
- redis首先fork出一个子进程
- 子进程根据当前数据集把新的aof写到一个临时文件里
- 主进程持续把新的变动写到内存的buffer里和旧的aof文件里(这样可以保证重写失败数据也不会丢失)
- 当子进程完成重写后给主进程一个信号,然后把内存里的buffer追加到新的aof里。
- 替换新旧aof文件
在我看来,主进程将新的变动写到旧的aof里是一个备用手段,这样可能降低了一部分性能,但保证了数据的安全
- 重写是直接把当前内存的数据生成对应命令,并不需要读取老的AOF文件进行分析、命令合并。
- 不管是RDB还是AOF都是先写入一个临时文件,然后通过
rename
完成文件的替换工作。
重写相关的命令
# Redis会记住自从上一次重写后AOF文件的大小(如果自Redis启动后还没重写过,则记住启动时使用的AOF文件的大小)。
# 如果当前的文件大小比起记住的那个大小超过指定的百分比,则会触发重写。
# 同时需要设置一个文件大小最小值,只有大于这个值文件才会重写,以防文件很小,但是已经达到百分比的情况。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 要禁用自动的日志重写功能,我们可以把百分比设置为0:
auto-aof-rewrite-percentage 0
# 手动触发重写
bgrewriteaof
数据损坏修复
如果因为某些原因(例如服务器崩溃)AOF文件损坏了,导致Redis加载不了,可以通过以下步骤进行修复:
-
备份AOF文件。
-
使用
redis-check-aof
命令修复原始的AOF文件:
$ redis-check-aof --fix
-
可以使用
diff -u
命令看下两个文件的差异。 -
使用修复过的文件重启Redis服务。
从RDB切换到AOF
-
备份一个最新的
dump.rdb
的文件,并把备份文件放在一个安全的地方。 -
运行以下两条命令:
$ redis-cli config set appendonly yes # 打开aof $ redis-cli config set save "" # 关闭rdb
-
确保数据跟切换前一致。
-
确保数据正确的写到AOF文件里。
-
如果有需要,更改redis.conf中启用aof和关闭rdb,因为命令行方式修改配置在重启Redis后就会失效。
第二条命令是用来禁用RDB的持久化方式,但是这不是必须的,因为你可以同时启用两种持久化方式。
备份
建议的备份方法:
- 创建一个定时任务,每小时和每天创建一个快照,保存在不同的文件夹里。
- 定时任务运行时,把太旧的文件进行删除。例如只保留48小时的按小时创建的快照和一到两个月的按天创建的快照。
- 每天确保一次把快照文件传输到数据中心外的地方进行保存,至少不能保存在Redis服务所在的服务器。
线上性能建议
- 如果redis数据不是特别重要或者可以通过其他方式重写生成数据,可以关闭持久化
- 自己制定策略定期检查redis情况,然后可以手动触发备份、重写数据
- 单机多个实例的话,要防止多个机器同时运行持久化或重写操作,防止出现内存、cpu、io资源竞争,让持久化变为串行
- 加入主从机器,从从机上进行备份
- rdb与aof可以同时存在,可以配合使用