Redis知识整合(二):数据的持久化
如果重启redis服务,或者重启redis所在的机器,那么对于将数据全部保存在内存中的redis而言,这两个操作无疑会让其数据全部丢失,此时如果我们没有做更多的容错方案,比如横向做集群、纵向做主从,此时就会造成
缓存雪崩
,所以我们或许需要给redis内的数据做个持久化,让其在恢复时读取持久化数据,恢复原始数据,redis有两种持久化方式,分别是RDB
和AOF
,RDB的实时性不如AOF,AOF恢复速度不如RDB
一、RDB
1.1:概览
RDB持久化会将当前redis进程内的数据生成快照保存到硬盘中(rdb文件),相关指令为bgsave
我们可以通过配置redis.conf
使其自动持久化:
1 | save <seconds> <changes> #表示seconds秒内存在changes次修改时,自动触发bgsave命令 |
在没有开启AOF
的情况下shutdown
掉redis,也会自动触发一次bgsave;
还有一种情况会自动触发bgsave,从节点发起全量复制时,主节点会自动触发bgsave并将生成的rdb文件发送给从节点。
1.2:过程
bgsave过程如下:
这是个重操作,好在不影响主进程,即便如此,bgsave也不能频繁执行,这也是rdb持久化方式无法做到秒级持久化的原因,采用这种方式持久化就要承担部分数据丢失的风险。
二、AOF
2.1:概览
针对rdb不适合实时持久化的问题,redis提供了aof
持久化方案来解决。
开启aof持久化的redis.conf配置:
1 | appendonly yes #这一项置为true(默认false) |
2.2:流程
aof的大体流程如下:
主要分为同步aof
文件和定期刷新aof
文件两大块,定期刷新文件时保证数据不丢失的做法是增量双写(1-1
)和旧数据同步(1-2
),其中1-2中批量同步新aof文件时,可以通过aof-rewrite-incremental-fsync
来控制每次同步的数据大小,默认32M。
aof重写文件的触发时机主要由以下两种方式控制:
- 手动调用
bgrewriteaof
命令 - 根据
auto-aof-rewrite-min-size
和auto-aof-rewrite-percentage
参数控制自动触发时机
auto-aof-rewrite-min-size:运行aof重写时aof文件的最小体积,默认64M
auto-aof-rewrite-percentage:当前aof文件的增量大小和上一次重写后aof文件大小的比值
根据以上描述,重写aof文件的自动触发条件为:
三、重启加载
不管是aof还是rdb,都是为了让redis重启时可以恢复原来的数据,整个过程如下:
可以看到,当rdb和aof同时启用时,优先使用aof.
四、优化
4.1:fork操作
无论是aof还是rdb,涉及到的fork操作都是重操作,其耗时取决于redis主进程中的内存大小(每次fork都需要复制主进程的内存页表),主进程内存每增加1GB将拖慢fork操作20ms,可以在info stats
统计中查看latest_fork_usec
指标获取最近一次fork操作的耗时。
改善fork耗时:
- 优先使用物理机或高效支持fork操作的虚拟化技术,避免使用
Xen
- 控制redis的最大可用内存(因为fork耗时跟内存量息息相关),生产环境建议redis实例内存控制在10G内
- 合理配置Linux内存分配策,避免物理内存不足导致的fork失败
- 降低fork操作的频率,如适当放宽aof的触发时机
4.2:子进程开销监控&优化
通过前面的了解,我们知道redis中的子进程主要负责aof和rdb文件的重写,它的运行过程主要涉及CPU、内存、磁盘三部分的消耗:
4.2.1:CPU
开销:子进程将进程内的数据分批次写入磁盘属于CPU密集型操作(对单核CPU的利用率可达90%)
优化:主要以减少资源竞争为优化点,比如:
- redis为CPU密集型服务,所以不要绑定单核CPU操作,因为子进程非常耗CPU,会严重影响父进程(单核上下文竞争)
- 要避免和其他CPU密集型服务部署在一起,避免过渡竞争CPU
- 多实例部署redis时,应保证同一时刻只有一个子进程在运行
4.2.2:硬盘
开销:子进程主要负责将aof或rdb文件写入硬盘,也就造成了硬盘写入压力(可通过系统工具sar、iostat、iotop等分析出重写期间硬盘的负载情况)
优化:
- 不要和其他高硬盘负载的服务部署在一起(比如存储服务、消息队列等)
- aof重写会消耗大量的硬盘IO,所以在重写期间应减少aof缓冲区往aof里fsync的操作(设置
no-appendfsync-no-rewrite
为true即可,表示在重写期间不做fsync操作,但这样也可能会丢失整个aof重写期间的数据) - 在开启aof且redis处于高流量写入场景时,若使用普通机械硬盘,则在aof同步硬盘时会发生瓶颈
- 对于单机多实例的部署,可以配置不同实例分盘存储aof,分摊硬盘写入压力
4.2.3:AOF追加阻塞
通过图2-1我们知道,被写入aof缓冲区的数据在everysec策略下会每隔1s调用系统的fsync写一次磁盘,既然是一次调用,那必然存在耗时,fsync是通过delay的方式定期触发的,每次redis主线程在发起fsync调用前都会判断上次fsync的时间,如果耗时超出2s,redis主线程便会陷入阻塞,等待上次fsync执行完,所以aof理论上最多会丢失2s的数据(通过info Persistence中的aof_delayed_fsync
可以查看aof同步任务是否发生了delay)。
避免这种情况发生的办法就是优化硬盘(参考4.2.2)