redis 持久化对比(RDB与AOF)
redis两种持久化方式:
- RDB(Redis Database):将内存中的数据作为一份快照保存到硬盘中
- AOF(Append Only File):每次执行命令都会都会尝试触发刷盘操作,实时性好,数据丢的少,但有性能损耗
RDB
概念
将redis的内存数据,保存一份到二进制文件中
触发方式
- 主动save:整个redis server会阻塞,直至完成
- bgsave:通过异步流程完成快照备份
- 自动触发:根据规则设置触发时机,m秒内发生n次修改(save m n)
bgsave流程
- fork出一个子进程
- 子进程和主进程共享内存(写时复制),子进程直接读取数据,并写到二进制文件中
- 替换原有的RDB
写时复制(cow)
父、子进程共享一块内存数据,当父进程要修改数据时,子进程才会将数据copy到自己的物理空间中
通过这种方式,这样能避免数据的大量copy,减少内存消耗
总结
优点
- 机制简单:简单的快照原理
- 写入损耗低:与aof不同,不用在每次写入后,还需要额外的写入aof_buf中
- 文件小:备份数据时压缩后的二进制文件,空间占用小
- 恢复速度快:基于二进制直接恢复
缺点
- 丢失数据:备份间隔宕机时,会丢失数据
- 备份时性能吃紧:虽然在一般的写入时无需额外的性能开销,但是在备份时会造成大量的io,消耗性能
AOF
概念
- redis每执行一次写命令,将数据追加到aof_buf中
- 根据appendfysnc设定的刷盘时机,将aof_buf中数据刷到磁盘上
刷新磁盘的时机(appendfysnc)
- always:每次都会落盘,性能极差,但不会丢数据
- no:不主动刷盘,交给操作系统刷盘,性能最好,但是丢失数据风险较大
- everysec:每秒定时刷一次,折衷方案
文件重写
aof文件需要定期重写,以免文件无限膨胀,减少故障时恢复的时间
通过直接读取redis,构造aof文件,可以有效的减少存储空间,主要表现在:
- 跳过过期、无效的key
- 将若干命令压缩成一条
- 有效的减少重复命令
重写的伪代码如下所示:
f:=createFile(path)
# 遍历 db
for db range redis.DB():
# 遍历 key
for key range db:
# 跳过过期 key
if key.isExpired():
continue
# 压缩 key
f.write(compressCmd(key))
# 设置超时时间
if key.hasExpireTime():
f.write(expiredCmd(key))
f.close()
压缩的方案主要讲若干条对同一个key的重复、散落的命令,压缩成一条命令,for example:
Lpush queue r1
Lpush queue r2
Lpush queue r2
可以被压缩成一条
Lpush queue r1 r2
子进程的后台重写
由于需要遍历线上的redis,直接同步处理的话,会阻塞主进程,从而拒绝访问,相当于停机copy数据(但是不用考虑重写过程中的写入问题,因为新的写入被阻塞了)
为了解决这一问题,诞生了基于子进程的后台重写:子进程通过使用主进程的数据副本,实现copy数据
在这种情况下,主进程仍在服务,无可避免会有新的写入产生。为了保证一致性,在文件重写时,会开辟一块新的buffer,用于存储重写过程中发生的变动,如下图所示:
在完成重写后,会进行收尾工作:
- 将aof文件重写buffer中的数据写到刚刚重写后的aof文件中
- 用重写后的aof文件替换现有的aof文件
注意为了保证数据一致性,这两个操作会阻塞主进程
恢复
相对简单,就是创建一个本地的伪客户端,读取aof文件,一次写入server的redis即可
总结
优点
- 容错性较好:跳过某条失败的命令后,可以继续恢复
- 易读:每条命令都是一条写入语句,可读性强
- 支持后台重写:避免文件无限膨胀
缺点
- 恢复速度较慢:一条一条插入语句,执行耗时
- 占用空间大:是完整的命令,所用空间当然也多
参考
转载自:https://juejin.cn/post/7029324234232954888