9.Redis混合持久化你不会没听说过吧?(高频面试题)

Redis持久化机制

Redis 的数据全部在内存里,如果突然宕机,数据就会全部丢失,因此必须有一种机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的持久化机制。

什么是持久化?
就是把内存里的数据保存到硬盘上。

必须使用数据持久化吗?
Redis的数据持久化机制是可以关闭的。如果你只把Redis作为缓存服务使用,Redis中存储的所有数据都不是该数据的主体而仅仅是同步过来的备份,那么可以关闭Redis的数据持久化机制。

但通常来说,仍然建议至少开启RDB方式的数据持久化,因为:
①数据量不是非常大时,RDB方式的持久化几乎不损耗Redis本身的性能,因为Redis父进程持久化时只需要fork一个子进程,这个子进程可以共享主进程的所有内存数据,子进程会去读取主进程的内存数据,并把它们写入RDB文件。
②Redis无论因为什么原因发送故障,重启时能够自动恢复到上一次RDB快照中记录的数据(自动加载RDB文件)。这省去了手工从其他数据源(如数据库)同步数据的过程,而且要比其他任何的数据恢复方式都要快。
③服务器的硬盘都是T级别的,几个G的数据影响忽略不计。

Redis 不同于 Memcached 的很重要一点就是,Redis 支持持久化,而且支持三种不同的持久化策略。

1.RDB

Redis提供了两个命令来生成 RDB 文件:

  • save:在主进程中执行,会导致写请求阻塞。
  • bgsave:fork一个子进程,专门用于写入 RDB 文件,避免了主进程的阻塞。

为了快照而阻塞写请求,这是系统无法接受的,因此Redis借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。

Redis在执行持久化时,会fork出一个bgsave子进程,这个子进程可以共享主进程的所有内存数据,bgsave子进程运行后,会去读取主进程的内存数据,并把它们写入RDB文件。

有小伙伴问,为什么要fork一个子线程?
redis是单线程程序,若单线程同时在服务线上的请求还需要进行文件IO操作,这不仅影响性能而且还会阻塞线上业务,因此这里主进程fork出一个进程,fork出的这个进程去完成快照操作。

快照持久化是 Redis 默认采用的持久化方式,我们可以根据业务需求配置下面参数:

save 900 1    #每900秒(15分钟)至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

save 300 10   #每300秒(5分钟)至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

save 60 10000 #每60秒(1分钟)至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

key发生变化(key数据添加、修改、删除)

触发快照的几种方式:
①服务器正常关闭时,会照一次快照 ./bin/redis-cli shutdown
②key满足一定条件,会照一次快照(通过上述Redis.conf配置)
③通过BGSAVE命令(在redis中执行)手动触发RDB快照保存

优点:
①RDB文件紧凑,体积小,网络传输快,适合全量复制。
②与AOF方式相比,通过RDB文件恢复数据比较快更快。
③RDB最大化了Redis的性能,因为Redis父进程持久化时只需要fork一个子进程,这个子进程可以共享主进程的所有内存数据,子进程会去读取主进程的内存数据,并把它们写入RDB文件。

缺点:
①快照是定期生成的,所有在 Redis 故障时或多或少会丢失一部分数据。
②当数据量比较大时,fork 的过程是非常耗时的,fork 子进程时是会阻塞的,在这期间 Redis 是不能响应客户端的请求的。

2.AOF

Redis会把每一个写请求都记录在一个日志文件里,在Redis重启时,会把AOF文件中记录的所有写操作顺序执行一遍,确保数据恢复到最新。

Redis 会在收到客户端修改指令后,先进行参数校验,如果没问题,就立即将该指令文本存储到 AOF 日志中,也就是先存到磁盘,然后再执行指令。这样即使遇到突发宕机,已经存储到 AOF 日志的指令进行重放一下就可以恢复到宕机前的状态。

日志文件太大怎么办?
AOF 日志在长期的运行过程中会变的很大,Redis重启时需要加载 AOF 日志进行指令重放,此时这个过程就会非常耗时。 所以需要定期进行AOF 重写,给 AOF 日志进行瘦身。

AOF如何重写?
Redis 提供了 bgrewriteaof 指令用于对 AOF 日志进行瘦身。每次执行重写时,主进程 fork 出一个bgrewriteaof 子进程,会把主进程的内存拷贝一份给 bgrewriteaof 子进程,对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中。序列化完毕后再将操作期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志文件了,瘦身工作就完成了。

Redis提供了AOF rewrite功能,可以重写AOF文件,只保留能够把数据恢复到最新状态的最小写操作集。

AOF 重写可以通过bgrewriteaof命令(在redis里执行)触发,也可以配置Redis定期自动进行:

## Redis在每次AOF rewrite时,会记录完成rewrite后的AOF日志大小,当AOF日志大小在该基础上增长了100%后,自动进行AOF rewrite。同时如果增长的大小没有达到64mb,则不会进行rewrite。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

AOF默认是关闭的,如果需要开启,需要在redis.conf配置文件中配置:

appendonly  yes

AOF提供三种fsync配置,always/everysec/no,通过配置项appendfsync指定,默认是everysec。

appendfsync always    # 每写入一条日志就进行一次fsync操作,数据安全性最高,但速度最慢(每次有数据修改发生时都会写入AOF文)
appendfsync everysec  # 折中的做法,交由后台线程每秒fsync一次(每秒钟同步一次,该策略为AOF的缺省策略)
appendfsync no        # 不进行fsync,将flush文件的时机交给OS决定,速度最快(从不同步。高效但是数据不会被持久化)

优点:
①数据安全性高,可以根据业务需求配置fsync策略
②AOF文件易读,可修改,在进行了某些错误的数据清除操作后,只要AOF文件没有rewrite,就可以把AOF文件备份出来,把错误命令删除,然后恢复数据

缺点:
①AOF方式生成的日志文件太大,即使通过AFO重写,文件体积仍然很大
②数据恢复速度比RDB慢

3.混合持久化

如果我们采用 RDB 持久化会丢失一段时间数据。如果我们采用 AOF 持久化,AOF日志较大,重放比较慢。

Redis 4.0 为了解决这个问题,支持混合持久化。将 RDB 文件的内容和增量的 AOF 日志文件存在一起。

混合持久化同样也是通过 bgrewriteaof 完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以 RDB 方式写入 AOF 文件,然后在将重写缓冲区的增量命令以 AOF 方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据。

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

4.实战经验

快照需要fork子进程的方式进行的,它是一个比较耗资源的操作。(当数据量非常大时,fork会很耗时,需要大概几百毫秒甚至1秒,fork时父进程是阻塞的,不能正常服务redis读写请求)

AOF 的 fsync 是一个耗时的 IO 操作,它会降低 Redis 性能,同时也会增加系统 IO 负担

所以通常 Redis 的主节点是不会进行持久化操作,持久化操作主要在从节点进行。从节点是备份节点,没有来自客户端请求的压力,它的操作系统资源往往比较充沛。

但是如果出现网络分区,从节点长期连不上主节点,就会出现数据不一致的问题,特别是在网络分区出现的情况下又不小心主节点宕机了,那么数据就会丢失,所以在生产环境要做好实时监控工作,保证网络畅通或者能快速修复。另外还应该再增加一个从节点以降低网络分区的概率,只要有一个从节点数据同步正常,数据也就不会轻易丢失。

你可能感兴趣的:(9.Redis混合持久化你不会没听说过吧?(高频面试题))