Day11_06_Redis教程之持久化方案实现及原理详解

06_Redis教程之持久化方案实现及原理详解

一. Redis持久化方案简介

Redis支持两种持久化存储方式: RDB(快照)和AOF. RDB是每隔一段时间存储一次文件,属于全量备份,RDB是内存数据的二进制序列化形式,在存储上非常紧凑;而AOF默认是每秒存储一次操作的写命令,属于连续的增量备份,AOF日志记录的是内存数据修改的写指令记.另外 AOF 日志在长期的运行过程中会变得无比庞大,数据库重启时需要加载 AOF 日志进行指令重放,这个时间就会无比漫长,所以需要定期进行 AOF 重写,给 AOF 日志进行瘦身.

Redis是支持持久化的内存数据库,也就是说redis需要经常将内存中的数据同步到硬盘来保证持久化.没有持久化的redis和memcache一样,相当于一个纯内存数据库.

二. RDB持久化方案

1. RDB方案简介

RDB持久化方案是将当前进程中的数据生成快照(一个临时文件),然后把这个快照保存到硬盘(因此也称作快照持久化),保存的文件后缀是rdb.

而当持久化结束后,在Redis重启的时候,会用这个快照替换上次持久化的文件,从而达到数据恢复的目的.

2. RDB持久化方案的实现

Redis默认会将数据在内存中以快照的方式写入到一个二进制文件中,该文件默认名称为dump.rdb.我们可以通过配置文件来设置自动做快照的持久化方式,比如可以配置redis在n秒内如果m个key修改,就自动做快照.

vim /usr/local/redis3.2.2/conf/redis.conf 修改配置文件

RDB默认就已开启,redis.conf中的具体配置参数如下:

save 900 1  #900秒内,超过1个key被修改,则发起快照保存;
save 300 10  #300秒内,超过10个key被修改,则发起快照保存;
save 60 10000  #60秒内,超过10000个key被修改,则发起快照保存.

dbfilename dump.rdb 持久化数据存储在本地的文件;

dir ./  持久化数据存储在本地的路径,如果是在/redis/redis-3.0.6/src下启动的redis-cli,则数据会存储在当前src目录下.

当满足save的条件时,比如更改了1个key,900s后会将数据写入临时文件,持久化完成后将临时文件替换旧的dump.rdb(存储数据的节点是到触发时间时的的节点).

3. 使用RDB恢复数据

自动持久化的数据存储到dump.rdb后,实际只要重启redis服务即可完成数据恢复(启动redis的server时会从dump.rdb中先同步数据).

使用命令进行持久化save存储:
./redis-cli -h ip -p port save
./redis-cli -h ip -p port 

bgsave
一个是在前台进行存储,一个是在后台进行存储.

4. RDB的持久化过程

RDB持久化的过程,相当于在执行bgsave命令.该命令执行过程如下图所示:

如图所示,主线程需要调用系统函数fork(),构建出一个子进程进行持久化.很不幸的是,在构建子进程的过程中,父进程就会阻塞,无法响应客户端的请求.

而且,在测试中发现,fork函数在虚拟机上较慢,真机上较快.考虑到现在都是部署在docker容器中,很少部署在真机上,为了性能,所以在master上不建议打开RDB持久化.

5. RDB方案的优点

1️⃣. RDB文件紧凑,所以适合备份,全量复制得场景.例如每 6 小时执行一次 bgsave,保存到文件系统之类的;

2️⃣. Redis 加载 RDB 恢复数据远远快于 AOF;

3️⃣.RDB使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了redis的高性能.

6. RDB方案的缺点

1️⃣.无法实现秒级持久化;

2️⃣.老版本的Redis无法兼容新版本的RDB;

3️⃣.RDB是每间隔一段时间进行一次持久化,如果持久化之间redis发生故障,数据可能会丢失,所以这种方式更适合对数据完整性要求不严格的时候.

7. RDB方案的特点

  • 1️⃣. RDB方案是一种快照模式,即保存的是key-value格式的数据内容;

  • 2️⃣. RDB 有 2 种持久方式,同步 save 模式和异步 bgsave 模式.由于 save 是同步的,所以可以保证数据一致性,而 bgsave 则不能;

  • 3️⃣. save 可以在客户端显式触发,也可以在 shutdown 时自动触发;bgsave 可以在客户端显式触发,也可以通过配置由定时任务触发,也可以在 slave 节点触发.

  • 4️⃣. save 导致 redis 同步阻塞,基本已经废弃.bgsave 则不会导致阻塞,但也有缺点: 在 fork 时,需要增加内存服务器开销,因为当内存不够时,将使用虚拟内存,导致阻塞 Redis 运行,所以需要保证空闲内存足够;

  • 5️⃣. 默认执行 shutdown 时,如果没有开启 AOF,则自动执行 bgsave;

  • 6️⃣. 每次的 RDB 文件都是替换的.

8. RDB方案的优化

Redis 会采用 LZF算法 压缩 RDB 文件,让最终的 RDB 文件远小于内存大小,默认开启,但会消耗 CPU.

9. RDB方案的实现原理

我们知道 Redis 是单线程程序,这个线程要同时负责多个客户端套接字的并发读写操作和内存数据结构的逻辑读写.

在服务线上请求的同时,Redis 还需要进行内存快照,内存快照要求 Redis 必须进行文件 IO 操作,可文件 IO 操作是不能使用多路复用 API.

这意味着单线程在服务线上请求的同时,还要进行文件 IO 操作,而文件 IO 操作会严重拖累服务器请求的性能.

还有个重要的问题,为了不阻塞线上的业务,Redis 就需要一边持久化,一边响应客户端的请求.持久化的同时,内存数据结构还在改变,比如一个大型的 hash 字典正在持久化,结果一个请求过来把它给删掉了,可是还没持久化完呢,这该怎么办呢?

Redis 使用操作系统的多进程 COW(Copy On Write)机制来实现快照持久化,这个机制很有意思,也很少人知道.多进程 COW 也是鉴定程序员知识广度的一个重要指标.

Redis 在持久化时会调用 glibc 的函数 fork 产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求.子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段.这时你可以把父子进程想象成一个连体婴儿,它们在共享身体.这是 Linux 操作系统的机制,为了节约内存资源,所以尽可能让它们共享起来.在进程分离的一瞬间,内存的增长几乎没有明显变化.

子进程做数据持久化,不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到磁盘中.但是父进程不一样,它必须持续服务客户端请求,然后对内存数据结构进行不间断的修改.这个时候就会使用操作系统的 COW 机制来进行数据段页面的分离.

数据段是由很多操作系统的页面组合而成,当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改.这时子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据.

随着父进程修改操作的持续进行,越来越多的共享页面被分离出来,内存就会持续增长,但是也不会超过原有数据内存的 2 倍大小.另外一个 Redis 实例里冷数据占的比例往往是比较高的,所以很少会出现所有的页面都会被分离,被分离的往往只有其中一部分页面.每个页面的大小只有 4KB,一个 Redis 实例里面一般都会有成千上万个页面.

子进程因为数据没有变化,它能看到的内存里的数据在进程产生的一瞬间就凝固了,再也不会改变,这也是为什么 Redis 的持久化叫“快照”的原因.接下来子进程就可以非常安心地遍历数据,进行序列化写磁盘了.

三. AOF方案

1. AOF持久化方案简介

AOF(Append Only File)持久化是将Redis执行过的写命令记录到一个单独的日志文件中,数据恢复时按照从前到后的顺序再将这些写命令再执行一遍,从而实现数据的恢复.

2. AOF持久化的同步(刷盘)策略

同步策略,或者说刷盘策略,也就是fsync. Redis官方的推荐配置是everysec,也就是Redis默认每隔一秒进行一次fsync调用,将缓冲区中的数据写到磁盘.

缓冲区同步(刷盘)策略,由参数 appendfsync 控制,一共3种:

  • 1️⃣.always: 会调用系统 fsync 函数,直到同步到硬盘返回响应结果,严重影响redis性能;

  • 2️⃣.everysec: 先调用操作系统的 write 函数,写到缓冲区,然后 redis再每秒执行一次操作系统的 fsync 函数,推荐使用这种方式;

  • 3️⃣.no: 只执行操作系统的 write函数,具体同步到硬盘策略由操作系统决定,不推荐该方案,因为数据很不安全,容易丢失数据.

然而,如果磁盘性能不稳定,fsync的调用时间超过1秒钟,此时主线程进行AOF的时候会对比上次fsync成功的时间:如果距上次不到2s,主线程直接返回;如果超过2s,则主线程阻塞直到fsync同步完成.
因此AOF也是会影响redis的性能的.

注意:

在linux中,wrtie函数将数据写入文件的时候,是将数据写入操作系统的缓冲区,并未真正将数据刷入磁盘.而fsync函数可以强制让操作系统把缓冲区里的数据刷入磁盘.

所以我们为了保证读写性能最大化,可以将master的持久化关闭.

3. AOF持久化的实现

#启动aof持久化
appendonly yes

# aof的3种刷盘(同步)策略:

#appendfsync always(最常用) //收到写命令就立即写入到磁盘,效率最慢,但是可以保证数据最大的完整性;

#appendfsync everysec(推荐值) //每秒写一次硬盘,在性能和持久化方面做了很好的折中;

#appendfsync no //完全依赖os,性能最好,持久化没保证.

重启redis发现bin/目录下多了一个appendonly.aof.

注意:

开启aof后之前的rdb模式就失效了,且之前的数据会被清空.

4. AOF方案的特点

  • 1️⃣.AOF的默认文件名是 appendonly.aof,和 RDB 一样,保存在配置文件中的 dir 目录下;

  • 2️⃣.AOF 相比于 RDB,每次都会保存写命令,数据实时性更高;

  • 3️⃣.AOF 由于每次都会记录写命令,文件会很大,因此需要进行优化,称之为“重写机制”;

  • 4️⃣.AOF 每次保存的写命令都会放在一个缓冲区,会根据不同的策略同步到磁盘.

5. AOF方案的优点

可以保持更高的数据完整性,如果设置追加file的时间是1s,此时redis发生故障,最多会丢失1s的数据;且如果日志写入不完整,也支持通过redis-check-aof来进行日志修复.AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的flushall).

6. AOF方案的缺点

随着时间的增长,这个AOF文件会越来越大,所以Redis内部有一套rewrite机制,来缩小AOF文件的体积.然而,在rewrite的过程中也是需要父进程来fork出一个子进程来进行rewrite操作,而fork操作会造成父进程阻塞,无法响应客户端的请求.

所以最终AOF文件比RDB文件大,且恢复速度慢.

类似于mysql日志,由于快照方式是在一定时间间隔做一次,所以如果发生redis意外宕机的情况,就会丢失最后一次快照后的所有被修改的数据.aof比快照方式有更好的持久化性,是由于redis在使用aof时,redis会将每一个收到的写命令都通过write函数追加到命令中,在redis重新启动时会重新执行文件中保存的写命令,然后在内存中重建这个数据库的内容,这个文件在redis/bin目录下,appendonly.aof.aof不是立即写到硬盘上,可以通过配置文件修改强制写到硬盘中.

7. AOF的“重写机制”详述

  • 1️⃣.首先父进程fork出一个子进程(类似于bgsave);

  • 2️⃣.然后主进程会将命令写到2个缓冲区,一个是原有的 “AOF 缓存区”,一个是专门为子进程准备的 “AOF 重写缓冲区”;

  • 3️⃣.子进程将命令写到新的 AOF 文件中,redis会批量创建新的aof文件,这些文件默认为32m大小,写完后通知主进程;

  • 4️⃣.主进程会把“AOF 重写缓冲区”里的数据写到新 AOF 文件中;

  • 5️⃣.最后将新的 AOF 文件替换老文件.

8. AOF方案的实现原理

AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的指令记录.

假设 AOF 日志记录了自 Redis 实例创建以来所有的修改性指令序列,那么就可以通过对一个空的 Redis 实例顺序执行所有的指令——也就是“重放”,来恢复 Redis 当前实例的内存数据结构的状态.

Redis 会在收到客户端修改指令后,进行参数校验、逻辑处理,如果没问题,就立即将该指令文本存储到 AOF 日志中,也就是说,先执行指令才将日志存盘.这点不同于 leveldb、hbase 等存储引擎,它们都是先存储日志再做逻辑处理.

Redis 在长期运行的过程中,AOF 的日志会越变越长.如果实例宕机重启,重放整个 AOF 日志会非常耗时,导致长时间 Redis 无法对外提供服务,所以需要对 AOF 日志瘦身.

Redis 提供了 bgrewriteaof 指令用于对 AOF 日志进行瘦身.其原理就是开辟一个子进程对内存进行遍历,转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中.序列化完毕后再将操作期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志文件了,瘦身工作就完成了.

AOF 日志是以文件的形式存在的,当程序对 AOF 日志文件进行写操作时,实际上是将内容写到了内核为文件描述符分配的一个内存缓存中,然后内核会异步将脏数据刷回到磁盘的.

这就意味着如果机器突然宕机,AOF 日志内容可能还没有来得及完全刷到磁盘中,这个时候就会出现日志丢失.那该怎么办?

Linux 的 glibc 提供了 fsync(int fd) 函数可以将指定文件的内容强制从内核缓存刷到磁盘.只要 Redis 进程实时调用 fsync 函数就可以保证 AOF 日志不丢失,但是 fsync 是一个磁盘 IO 操作,它很慢!如果 Redis 执行一条指令就要 fsync 一次,那么 Redis 高性能的地位就不保了.

所以在生产环境的服务器中,Redis 通常是每隔 1s 左右执行一次 fsync 操作,这个 1s 的周期是可以配置的.这是在数据安全性和性能之间做的一个折中,在保持高性能的同时,尽可能使得数据少丢失.

Redis 同样也提供了另外两种策略,一个是永不 fsync---让操作系统来决定何时同步磁盘,这样做很不安全;另一个是来一个指令就 fsync 一次——结果导致非常慢.这两种策略在生产环境中基本很少使用,了解一下即可.

四. Redis的持久化恢复

AOF 和 RDB 文件都可以用于服务器重启时的数据恢复,具体流程如下图:

从图中可以看出Redis优先加载 AOF文件,当没有 AOF 时才加载 RDB.当 AOF 或者 RDB 存在错误时,则加载失败.

五. 两种持久化策略对比

1. RDB的优势

1️⃣.一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的.比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据.通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复.

2️⃣.对于灾难恢复而言,RDB是非常不错的选择.因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上.

3️⃣.性能最大化.对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了.

4️⃣.相比于AOF机制,如果数据集很大,RDB的启动效率会更高.

2. RDB的劣势

1️⃣.如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择.因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失.

2️⃣.由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟.

3. AOF的优势

1️⃣.该机制可以带来更高的数据安全性,即数据持久性.Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步.事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失.而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中.可以预见,这种方式在效率上是最低的.

2️⃣.由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容.然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题.

3️⃣.如果日志过大,Redis可以自动启用rewrite机制.即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行.因此在进行rewrite切换时可以更好的保证数据安全性.

4️⃣.AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作.事实上,我们也可以通过该文件完成数据的重建.

4. AOF的劣势

1️⃣.对于相同数量的数据集而言,AOF文件通常要大于RDB文件,RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快.

2️⃣.根据同步策略的不同,AOF在运行效率上往往会慢于RDB.总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效.

二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb).

六. 生产环境中的持久化方案选择

一般我们在生产环境中采用的持久化策略为:

  • 1️⃣.master关闭持久化;

  • 2️⃣.slave开启RDB,必要的时候AOF和RDB都开启.

该策略能够适应绝大部分场景和绝大部分集群架构.

1. 为什么是绝大部分场景?

因为这套策略存在部分数据丢失的可能性.redis的主从复制是异步的,master执行完客户端请求的命令后会立即返回结果给客户端,然后通过异步的方式把命令同步给slave,因此master可能还未来得及将命令传输给slave,就宕机了,此时slave变为master,数据就丢失了.

幸运的是,绝大部分的业务场景,都能容忍数据的部分丢失.即使真的遇到缓存雪崩的情况,代码中也有熔断器来进行资源保护,不至于所有的请求都转发到数据库而导致我们的服务崩溃.

2. 为什么是绝大部分集群架构?

因为在集群中存在redis读写分离的情况下,就不适合这套方案了.

由于采用redis读写分离架构,就必须要考虑主从同步的延迟性问题.目前业内采用redis读写分离架构的项目,真的太少了.

3. 为什么master关闭持久化?

原因很简单,因为无论哪种持久化方式都会影响redis的性能,哪一种持久化都会造成CPU的卡顿,影响对客户端请求的处理.为了保证最佳的读写性能,所以将master的持久化关闭!

4. 为什么slave开启RDB,而在必要的时候AOF和RDB都开启?

Redis官方是不推荐单独开启AOF的,因为基于AOF持久化的数据恢复太慢.

官方文档参考地址:
https://redis.io/topics/persistence

截图如下:

另外我们已经做了主从复制,数据已经实现备份,为什么slave还需要开持久化?

如果某一天可能进行某项工程的施工,把机房的电线挖断了,这就会导致master和slave机器同时宕机.

那么这个时候,我们需要迅速恢复集群,而RDB文件文件小、恢复快,因此灾难恢复常用RDB文件.

如果对数据安全性要求实在是比较高,可以将AOF和RDB持久化都开启.

另外,做好灾难备份,利用linux的scp命令,定期将rdb文件拷贝到云服务器上.

注意:

scp是secure copy的简写,用于在Linux下进行文件的远程拷贝的命令.和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的.

七. Redis 4.0新特性--混合持久化

重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据.通常使用 AOF 日志重放,但是重放 AOF 日志的性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间.

Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化.将 rdb 文件的内容和增量的 AOF 日志文件存在一起,这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小.

于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志,就可以完全替代之前的 AOF 全量文件重放,重启效率因此得到大幅提升.

八. Redis持久化的问题排查和性能优化

Redis 持久化是影响 Redis 性能的高发地,也是面试中常问的一大块.

1. fork 操作

当 Redis 做 RDB 或者 AOF 重写时,必然要进行 fork 操作.对于操作系统来说,fork 是一个重量级操作.而且,fork 还会拷贝一些数据,虽然不会拷贝主进程里所有的物理空间,但会复制主进程的空间内存页表.假如有一个 10GB 的 Redis 进程,需要复制大约 20MB 的内存页表,因此 fork 操作的耗费时长跟进程总内存量息息相关.再加上,如果使用虚拟化技术,例如 Xen 虚拟机,fork 会更加耗时.

一个正常的 fork 耗时大概在 20毫秒左右.为什么呢?假设一个 Redis 实例的 OPS 在 5 万以上,如果 fork 操作耗时在秒级,那么将拖慢几万条命令的执行,对生产环境影响明显,

我们可以在 Info stats 统计中查询 latestforkusec 指标来获取最近一次 fork 操作的耗时,单位微秒.

1.1 如何对fork优化

  • 1️⃣.优先使用物理机或者高效支持 fork 的虚拟化技术,避免使用 Xen;

  • 2️⃣.控制 redis 实例的最大内存,尽量控制在 10GB 以内;

  • 3️⃣.合理配置 Linux 内存分配策略,避免内存不足导致 fork 失败;

  • 4️⃣.降低 fork 的频率,如适度放宽 AOF 自动触发时机,避免不必要的全量复制.

2. 子进程开销的优化

fork 完毕之后,会创建子进程,子进程负责 RDB 或者 AOF 重写,这部分过程主要涉及到 CPU/内存/硬盘三个地方的优化.

  • 1️⃣.CPU开销分析: CPU 写入文件的过程是 CPU 使用密集的过程,通常子进程对单核 CPU 利用率接近 90%. 那么如何优化呢? 既然是 CPU 密集型操作,就不要绑定单核 CPU,因为这样会和父 CPU 进行竞争.同时,不要和其他 CPU 密集型服务部署在同一个机器上.如果部署了多个 Redis 实例,尽力保证同一时刻只有一个子进程执行重写工作;

  • 2️⃣.内存开销分析: 内存子进程通过 fork 操作产生,占用内存大小等同于父进程,理论上需要两倍的内存完成持久化操作,但 Linux 有 copy on write 机制,父子进程会共享相同的物理内存页,当父进程处理写操作时,会把要修改的页创建对应的副本,而子进程在 fork 操作过程中,共享整个父进程的内存快照.即如果重写过程中存在内存修改操作,父进程负责创建所修改内存页的副本,这里就是内存消耗的地方.如何优化呢?尽量保证同一时刻只有一个子进程在工作;避免大量写入时做重写操作.

  • 3️⃣.硬盘开销分析: 子进程主要职责是将 RDB 或者 AOF 文件写入硬盘进行持久化,势必对硬盘造成压力,可通过工具例如 iostat, iotop 等,分析硬盘负载情况.

2.1 如何优化:

  • 1️⃣.不要和其他高硬盘负载的服务放在一台机器上,例如 MQ,存储;

  • 2️⃣.AOF 重写时会消耗大量硬盘的 IO,可以开启配置 no-appendfsync-on-rewrite,默认关闭.表示在 AOF 重写期间不做 fsync 操作;

  • 3️⃣.当开启 AOF 的 Redis 在高并发场景下,如果使用普通机械硬盘,每秒的写速率是 100MB左右,这时Redis 的性能瓶颈在硬盘上,建议使用 SSD;

  • 4️⃣.对于单机配置多个 Redis 实例的情况,可以配置不同实例分盘存储 AOF 文件,分摊硬盘压力.

3. AOF 追加阻塞

当开启 AOF 持久化时,常用的同步硬盘的策略是“每秒同步” everysec,用于平衡性能和数据安全性.对于这种方式,redis 使用另一条线程每秒执行 fsync 同步硬盘,当系统资源繁忙时,将造成 Redis 主线程阻塞.

流程图如下:

通过上图可以发现: everysec 配置最多可能丢失 2 秒数据,不是 1 秒;如果系统 fsync 缓慢,将会导致 Redis 主线程阻塞影响效率.

3.1 问题定位:

  • 1️⃣.发生 AOF 阻塞时,会输入日志,用于记录 AOF fsync 阻塞导致拖慢 Redis 服务的行为;

  • 2️⃣.每当 AOF 追加阻塞事件发生时,在 info Persistence 统计中,aofdelayedfsync 指标会累加,查看这个指标方便定位 AOF 阻塞问题;

  • 3️⃣.AOF 同步最多运行 2 秒的延迟,当延迟发生时说明硬盘存在性能问题,可通过监控工具 iotop 查看,定位消耗 IO 的进程.

4. 单机多实例部署的优化

Redis 单线程架构无法充分利用多核CPU,通常的做法是一台机器上部署多个实例,当多个实例开启 AOF 后,彼此之间就会产生CPU 和 IO 的竞争.

4.1 如何解决这个问题呢?

让所有实例的 AOF 串行执行.

我们通过 info Persistence 中关于 AOF 的信息写出 Shell 脚本,然后串行执行实例的 AOF 持久化.

整个过程如图:

通过不断判断 AOF 的状态,手动执行 AOF 重写,保证 AOF 不会存在竞争.

九. 总结

持久化一直是影响 Redis 性能的高发地,也是面试中经常被问到的,包括 RDB的特点和优缺点,AOF 的特点和优缺点.事实上,由于 RDB 的数据实时性问题,目前使用 AOF 方案比较多,而持久化的恢复也是优先加载AOF文件.

关于持久化问题的排查,就很麻烦了,但无非几个方面: fork 耗时问题,子进程的 CPU/内存/硬盘等的开销,AOF 的同步阻塞,单机多实例部署等.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一一哥Sun

您的鼓励是我继续创作的动力哦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值