4

Redis知识整合(二):数据的持久化

 2 years ago
source link: https://exceting.github.io/2021/05/21/Redis%E7%9F%A5%E8%AF%86%E6%95%B4%E5%90%88%EF%BC%88%E4%BA%8C%EF%BC%89%EF%BC%9A%E6%95%B0%E6%8D%AE%E7%9A%84%E6%8C%81%E4%B9%85%E5%8C%96/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Redis知识整合(二):数据的持久化

如果重启redis服务,或者重启redis所在的机器,那么对于将数据全部保存在内存中的redis而言,这两个操作无疑会让其数据全部丢失,此时如果我们没有做更多的容错方案,比如横向做集群、纵向做主从,此时就会造成缓存雪崩,所以我们或许需要给redis内的数据做个持久化,让其在恢复时读取持久化数据,恢复原始数据,redis有两种持久化方式,分别是RDBAOF,RDB的实时性不如AOF,AOF恢复速度不如RDB

一、RDB

1.1:概览

RDB持久化会将当前redis进程内的数据生成快照保存到硬盘中(rdb文件),相关指令为bgsave

我们可以通过配置redis.conf使其自动持久化:

save <seconds> <changes> //表示seconds秒内存在changes次修改时,自动触发bgsave命令
dbfilename dump.rdb //这里指定保存的文件名

在没有开启AOF的情况下shutdown掉redis,也会自动触发一次bgsave;

还有一种情况会自动触发bgsave,从节点发起全量复制时,主节点会自动触发bgsave并将生成的rdb文件发送给从节点。

1.2:过程

bgsave过程如下:

这是个重操作,好在不影响主进程,即便如此,bgsave也不能频繁执行,这也是rdb持久化方式无法做到秒级持久化的原因,采用这种方式持久化就要承担部分数据丢失的风险。

二、AOF

2.1:概览

针对rdb不适合实时持久化的问题,redis提供了aof持久化方案来解决。

开启aof持久化的redis.conf配置:

appendonly yes //这一项置为true(默认false)
appendfilename "appendonly.aof" //这里指定保存的文件名

2.2:流程

aof的大体流程如下:

主要分为同步aof文件和定期刷新aof文件两大块,定期刷新文件时保证数据不丢失的做法是增量双写(1-1)和旧数据同步(1-2),其中1-2中批量同步新aof文件时,可以通过aof-rewrite-incremental-fsync来控制每次同步的数据大小,默认32M。

aof重写文件的触发时机主要由以下两种方式控制:

  • 手动调用bgrewriteaof命令
  • 根据auto-aof-rewrite-min-sizeauto-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)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK