Redis持久化和数据数据恢复
众所周知Redis的数据都是保存在内存中,如果没有持久化,Redis重启后数据就会丢失。 于是需要开启Redis的持久化功能,将数据保存到磁盘上,当redis重启后,可以从磁盘中恢复数据。
# Redis持久化存储
Redis提供了两种持久化方式:RDB和AOF,默认是采用RDB模式进行持久化。
# RDB
RDB是Redis默认的持久化方式,会在对应的目录下生产一个dump.rdb(可配置)的二进制文件,重启后通过加载dump.rdb文件恢复数据。
RDB文件生成有两种方式:
- 手动备份:save/bgsave命令
- 自动备份:在配置文件设置自动备份间隔条件,按照间隔条件自动生成
# 手动备份
# save命令
save指令会阻塞当前Redis进程,执行save
指令期间,Redis不能处理其他命令,直到rdb过程完成为止。
# bgsave命令
bgsave指令会在后台异步执行,Redis进程执行fork操作创建子进程,子进程负责rdb过程,完成后自动结束,此时Redis主进程仍然可以响应客户端请求。
# 自动备份
自动备份是在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程和执行bgsave命令一样,先fork一个子进程,将当前Redis数据写入临时文件,写入成功后,再替换之前的文件。
# RDB快照工作流程
- fork子进程;
- 子进程将数据集写入到一个临时RDB文件中;
- 当子进程完成对新RDB文件的写入时,Redis用新RDB文件替换原来的RDB文件,并删除旧的RDB文件;
redis.conf中关于RDB相关配置:
stop-writes-on-bgsave-error no # 创建快照失败是否继续写命令
rdbcompression yes # 生成的rdb备份文件是否进行压缩
rdbchecksum yes # 导入时是否检查
dbfilename dump.rdb # rdb备份文件名
dir /usr/local/redis/data/ # rdb备份文件存放路径
## 自动备份相关
save 900 1 # 15分钟内只要有1个key变化,则执行备份
save 300 10 # 5分钟内只要有10个key变化,则执行备份
save 60 10000 # 1分钟内只要有10000个key变化,则执行备份
2
3
4
5
6
7
8
9
10
另外,执行flushall()命令或者执行shutdown()命令,也会触发rdb快照备份。
# RDB的优势和劣势
# 优势
- 单一备份文件,容灾恢复方便,RDB备份文件加载速度快;
# 劣势
- 无法实时持久化,如果两次持久化之间redis发生故障,会发生数据丢失;
- 老版本Redis服务无法兼容新版RDB格式;
- RDB需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求;
# AOF
aof(append only file):将Reids的操作日志(写/删)以追加的方式写入文件。
redis.conf中关于AOF相关配置:
appendonly no # 是否开启aof,默认为no
appendfilename "appendonly.aof" # aof日志文件名称
appendfsync everysec # 多久记录一次数据,默认为`everysec(每秒)`
2
3
appendfsync的选项有三种:
- always:同步持久化,每次发生数据变化都会被立即记录, 能保证数据的完整性,但是性能相对较差,不推荐
- everysec:每秒钟记录一次
- no: 执行写操作之后由操作系统自动的去同步到磁盘,不推荐
主线程在执行fsync时,会对比上次fsync成功的时间;如果距上次不到2s,主线程直接返回;如果超过2s,则主线程阻塞直到fsync同步完成,因此数据最多丢失2s。fsync同步时间较长,说明硬盘存在性能问题,需要检查硬盘。
aof由于采用文件追加方式,会导致aof日志文件会越来越大,举个例子,如果你对一个计数器调用了100次INCR,那么仅仅是为了保存这个计数器的当前值,AOF文件就需要使用100条记录。然而在实际上,只使用一条SET命令已经足以保存计数器的当前值了,其余99条记录实际上都是多余的,另外还有一些过期的数据,无效的数据也都是可以去除。
过大的AOF文件不仅会影响服务器的正常运行,也会导致数据恢复需要的时间过长。为了处理这种情况,Redis支持对AOF文件进行重写。执行bgrewriteaof
命令,Redis将生成一个新的AOF文件,这个文件包含重建当前数据集所需的最少命令。
# 重写机制
当aof文件大小超过阈值,就会触发Redis的重写机制,生成新的aof文件,同时会对命令进行合并重写。
重写机制步骤:
- fock子进程;
- 子进程完成AOF重写的过程(将redis内存数据进行一次回溯成写到新的AOF文件中);
- 主进程继续讲操作记录到原有aof_buf当中;
- 主进程同时会将重写时间段内的操作记录到aof_rewrite_buf中;
- 当新的AOF文件生成之后,子进程将通知朱进程,主进程会将aof_rewrite_buf写入到新的AOF文件中;
- 将新的AOF文件替换就的AOF文件
由主进程把aof_rewrite_buf缓存追加到新日志文件, 主进程追加日志时,不会处理其他请求,如果aof_rewrite_buf特别大,例如几百M,也可能造成Redis几秒甚至几十秒不响应。
redis.conf中关于重写机制的相关配置
no-appendfsync-on-rewrite no # aof重写期间是否执行fsync同步
auto-aof-rewrite-percentage 100 # aof文件增长比例,指当前aof文件比上次重写的增长比例大小
auto-aof-rewrite-min-size 64mb # aof文件重写最小的文件大小
aof-load-truncated yes # 加载aof时如果有错如何处理
aof-rewrite-incremental-fsync yes # 文件重写策略
2
3
4
5
# AOF的优势和劣势
# 优势
- 数据保存度高
# 劣势
- AOF文件比RDB文件大,且恢复速度慢;数据集大的时候,比rdb启动效率低。
- 根据同步策略的不同,AOF在运行效率上往往会慢于RDB
# Redis性能问题
# fock()
fork()使用写时拷贝(COW,copy on write)实现。写时拷贝是一种可以推迟甚至免除拷贝数据的技术。fork()操作时并不复制整个父进程地址空间,而是让父进程和子进程共享同一个拷贝。只有在需要写入的时候,数据才会被复制,从而使各个进程拥有各自的拷贝。也就是说,资源的复制只有在需要写入的时候才进行,在此之前,只是以只读方式共享。这种技术使地址空间上的页的拷贝被推迟到实际发生写入的时候。所以就算fork很大内存的进程,对内存的消耗和耗时都很小。
现在虽然fork时,子进程不会复制父进程的数据空间,但是会复制内存页表(页表相当于内存的索引、目录);父进程的数据空间越大,内存页表越大,fork时复制耗时也会越多。这个问题也是导致Redis内存不宜过大的原因之一。
# 硬盘IO
当Redis实例QPS过大时,如果fsync频率过高或者rdb快照生成过快,会有高频的磁盘写操作,硬盘的性能将会对Redis造成影响。
# 优化方案
- 控制redis实例最大内存,最好不要超过10G
- 降低fork的频率,如适度放宽AOF/RDB自动触发时机,避免不必要的全量复制
- AOF重写时会消耗大量硬盘IO,可以开启配置no-appendfsync-on-rewrite,默认关闭。表示在AOF重写期间不做fsync操作
- Redis的性能瓶颈在硬盘上,建议使用SSD
- 对于单机配置多个Redis实例的情况,可以配置不同实例分盘存储AOF文件,分摊硬盘压力;
- 采用Redis主从,master关闭持久化
# Redis数据恢复
如果需要恢复数据,只需将备份文件(dump.rdb/appendonly.aof)移动到redis数据目录并启动服务即可。
优先级: aof > rdb