首页 > 首页 > 数据库 > 非关系型数据库 > Redis > Redis 架构原理01:持久化机制和实现原理
2024
05-22

Redis 架构原理01:持久化机制和实现原理

一、Redis 持久化的意义

redis的数据全部在内存中,如果突然宕机,数据就会全部丢失,因此必须有一种机制来保证redis的数据发生突发状况时不会丢失、或者只丢失少量,于是必须根据一些策略来把redis内存中的数据写到磁盘中,这样当redis服务重启时,就会将硬盘中的数据恢复到内存中。Redis持久化的意义就是为了保证突然宕机,内存数据不会全部丢失。

Redis 架构原理01:持久化机制和实现原理 - 第1张  | 架构迷

二、Redis 持久化机制

redis有两种持久化机制:RDB和AOF。每种持久化机制各有优缺点,了解每种持久化机制的原理对我们使用Redis有很大的帮助。Redis4.0后支持RDB和AOF两种持久化机制混合使用,所以存在三种持久化策略。

(1)、RDB持久化

RDB是基于快照一次的全量备份,即周期性的把redis当前内存中的全量数据写入到一个快照文件中(周期时间可以通过配置来调整)。redis是单线程程序,这个线程要同时负责多个客户端的读写请求,还要负责周期性的把当前内存中的数据写到快照文件中RDB中,数据写到RDB文件是IO操作,IO操作会严重影响redis的性能,甚至在持久化的过程中,读写请求会阻塞,为了解决这些问题,Redis采用多进程来同时进行读写请求和持久化操作。这样又会导致另外的问题,持久化的过程中,内存中的数据还在改变,假如redis正在进行持久化一个大的数据结构,在这个过程中客户端发送一个删除请求,把这个大的数据结构删掉了,这时候持久化的动作还没有完成,那么redis该怎么办呢?

redis使用操作系统的多进程COW(Copy On Write)机制来实现快照的持久化,在持久化过程中调用 glibc(Linux下的C函数库) 的函数fork()产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端的读写请求。子进程刚刚产生时,和父进程共享内存里面的代码段和数据段,这是Linux操作系统的机制,为了节约内存资源,所以尽可能让父子进程共享内存,这样在进程分离的一瞬间,内存的增长几乎没有明显变化。

1、linux中若父进程退出,子进程尚未结束,子进程不会立即退出。
在linux中,所有进程都有一个共同的父进程systemd,如果父进程退出了,子进程还没运行结束,子进程会被systemd(老版本的linux是initd)收养。

2、RDB持久化时,Redis会fork()一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端的读写请求。

子进程对当前内存中的数据进行持久化时,并不会修改当前的数据结构,如果父进程收到了读写请求,那么会把处理的那一部分数据复制一份到内存,对复制后的数据进行修改。所以即使对某个数据进行了修改,redis持久化到RDB中的数据也是未修改的数据,这也是把RDB文件称为"快照"文件的原因,子进程所看到的数据在它被创建的一瞬间就固定下来了,父进程修改的某个数据只是该数据的复制品。

实际上,内存中的全量数据由一个个的"数据段页面"组成,每个数据段页面的大小为4K,客户端要修改的数据在哪个页面中,就会复制一份这个页面到内存中,这个复制的过程称为"页面分离",在持久化过程中,随着分离出的页面越来越多,内存就会持续增长,但是不会超过原内存的2倍,因为在一次持久化的过程中,几乎不会出现所有的页面都会分离的情况,读写请求针对的只是原数据中的小部分,大部分redis数据还是"冷数据"。RDB过程整个过程如下图:

Redis 架构原理01:持久化机制和实现原理 - 第2张  | 架构迷

热点数据和冷数据是什么

  • 热点数据:读取频率高,如果不做缓存,给数据库造成很大的压力,可能被击穿。
  • 冷数据:读取频率低,数据设置缓存后有可能没有被访问就被挤出内存(超时dele)。

RDB有两种触发方式:自动触发和手动触发。

(1)自动触发:在 redis.conf 配置文件中的 SNAPSHOTTING 下配置save参数,来触发Redis的 RDB 持久化条件,也就是什么时候将内存中的数据保存到硬盘。比如“save m n”:表示m秒内数据集存在n次修改时,自动触发bgsave。

//默认如下配置:
save 900 1:每隔900s(15min),如果有超过1个key发生了变化,就写一份新的RDB文件
save 300 10:每隔300s(5min),如果有超过10个key发生了变化,就写一份新的RDB文件
save 60 10000:每隔60s(1min),如果有超过10000个key发生了变化,就写一份新的RDB文件
(配置多种策略可以同时生效,无论满足哪一种条件都会写一份新的RDB文件)

(2)手动触发:手动生成新的RDB文件,执行Redis的save、bgsave命令。

save:阻塞主进程,直到生成新的RDB文件;执行save命令期间,Redis不能处理其他命令。
bgsave:异步生成RDB文件,fork子进程去生成新的RDB文件,主进程不阻塞。

(2)、AOF持久化

AOF(Append-only file)日志存储的是redis服务器的顺序指令序列,即对内存中数据进行修改的指令记录。当redis收到客户端修改指令后,先进行参数校验,如果校验通过,先把该指令存储到AOF日志文件中,也就是先存到磁盘,然后再执行该修改指令。

redis把操作指令追加到AOF文件这个过程,并不是直接写到AOF文件中,而是先写到操作系统的内存缓存中,这个内存缓存是由操作系统内核分配的,然后操作系统内核会异步地把内存缓存中的redis操作指令刷写到AOF文件中。当redis宕机后重启后,可以读取该AOF文件中的指令,进行数据恢复,恢复的过程就是把记录的指令再顺序执行一次,这样就可以恢复到宕机之前的状态。

这儿存在一个问题,假如内存缓存中的redis指令还没有来得及刷写到AOF文件中就宕机了,那么这部分未刷写的指令就会丢失,不过,glibc函数库提供了 fsync() 函数,该函数可以将指定文件的内容强制从内存缓存中刷写到磁盘上。fsync操作的周期对redis的性能有很大影响,如何配置将在本文后续的内容中给出建议。AOF过程如下图:

Redis 架构原理01:持久化机制和实现原理 - 第3张  | 架构迷

redis在长期运行过程中,AOF日志会越来越大,如果redis服务重启后根据很大的AOF文件来顺序执行指令,将会非常耗时,导致redis服务长时间无法对外提供服务,所以需要对AOF文件进行"瘦身"。"瘦身"的过程称作AOF重写(rewrite)。

AOF Rewrite 的原理是,主进程fork一个子进程,对当前内存中的数据进行遍历,转换成一系列的redis操作指令,并序列化到一个新的AOF日志中,然后把序列化操作期间新收到的操作指令追加到新的AOF文件中,追加完毕后就立即替换旧的AOF文件,这样就完成了"瘦身"工作,即AOF Rewrite。AOF Rewrite过程如下图:

Redis 架构原理01:持久化机制和实现原理 - 第4张  | 架构迷

AOF文件重写后为什么会缩减文件大小

  • 过期的数据不再写入文件
  • 无效的命令不再写入文件:如有些数据被重复设值(set mykey v1, set mykey v2)、有些数据被删除了(sadd myset v1, del myset) 等。
  • 多条命令可以合并为一个:如sadd myset v1, sadd myset v2, sadd myset v3可以合并为sadd myset v1 v2 v3。
#1、AOF持久策略:fsync的策略,默认为everysec
appendfsync everysec
# everysec:每秒fsync一次,如果Redis宕机,1秒内的指令丢失。
# no:redis不主动fsync,完全交由操作系统决定
# always:1条指令fsync一次,保证数据一条不丢失

(3)、混合持久化

redis-4.x后支持了RDB和AOF混合使用。重启redis时,我们很少使用RDB来恢复内存状态,因为会丢失大量数据。我们通常使用AOF日志重放,但是重放AOF日志性能相对RDB来说要慢很多,这样在redis实例很大的情况下,启动需要花费很长的时间。redis-4.0为了解决这个问题,带来了一个新的持久化选项——混合持久化。将RDB文件的内容和增量的AOF日志文件存在一起,这里的AOF日志不再是全量 的日志,而是RDB久化开始 到 RDB持久化结束的这段时间发生的增量AOF日志,通常这部分AOF日志很小。redis-4.x混合持久化机制如下图:

Redis 架构原理01:持久化机制和实现原理 - 第5张  | 架构迷

三、redis 持久化机制对比

(1)、RDB的优缺点

优点:

  • RDB会生成多个数据文件,每个数据文件都代表了某一个时刻中redis的数据,这种多个数据文件的方式,非常适合做冷备,可以将这种完整的数据文件发送到一些远程的安全存储上去。
  • 生成RDB文件的时候,主进程不需要进行任何磁盘IO操作。当进行RDB持久化时,对redis服务处理读写请求的影响非常小,可以让redis保持高性能,因为redis主进程只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可。生成一次RDB文件的过程就是把当前时刻内存中的数据一次性写入文件中,而AOF则需要先把当前内存中的小量数据转换为操作指令,然后把指令写到内存缓存中,然后再刷写入磁盘。
  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。AOF存放的是指令日志,做数据恢复的时候,要回放和执行所有的指令日志,从而恢复内存中的所有数据;而RDB,就是一份数据文件,恢复的时候,直接加载到内存中即可。

缺点:

  • RDB方式数据没办法做到实时持久化/秒级持久化,会导致数据丢失。一般来说,RDB数据快照文件,都是每隔5分钟,或者更长时间生成一次,这个时候就得接受一旦redis进程宕机,那么会丢失最近5分钟的数据。这个问题,也是RDB最大的缺点,就是不适合做第一优先的恢复方案,如果你依赖RDB做第一优先恢复方案,会导致数据丢失的比较多。
  • RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题(版本不兼容)。
  • RDB每次在fork子进程来执行RDB快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,甚至数秒。所以一般不要让生成RDB文件的间隔太长,否则每次生成的RDB文件太大了,对redis本身的性能会有影响。

(2)、AOF的优缺点

优点:

  • AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据。
  • AOF日志文件以append-only模式(追加)写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破损,也很容易修复。
  • AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。因为在rewrite的时候,会对其中的指令进行压缩,会创建出一份需要恢复数据的最小日志出来。
  • AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据。

缺点:

  • 对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大。因为AOF是指令文件,RDB是二进制文件。
  • AOF的写性能比RDB的写性能低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次fsync,性能也还是很高的,只不过比起RDB来说性能低,如果要保证一条数据都不丢,也是可以的,AOF的fsync设置成每写入一条数据,fsync一次,但是这样,redis的性能会大大下降。
  • 基于AOF文件做恢复的速度不如基于RDB文件做恢复的速度。

(3)、混合持久化的优缺点

优点:结合了RDB和AOF的优点,使得数据恢复的效率大幅提升

缺点:兼容性不好,redis-4.x新增,虽然最终的文件也是.aof格式的文件,但在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,阅读性较差。

(4)、如何选择redis持久化机制

RDB和AOF到底该如何选择

不要仅仅使用RDB,因为那样会导致丢失很多数据。

也不要仅仅使用AOF,一是数据恢复慢,二是可靠性也不如RDB,毕竟RDB文件中存储的就是某一时刻实实在在的数据,而AOF只是操作指令,把数据转换为操作指令不一定是百分百没问题的。

综合使用AOF和RDB两种持久化机制,用AOF来保证数据不丢失,作为数据恢复的第一选择; 用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB来进行快速的数据恢复。

(5)、AOF和RDB同时工作

redis在写RDB文件的时候不会执行AOF rewrite;redis在执行AOF rewrite的时候不会生成新的RDB。

如果redis正在生成新的RDB文件,此时用户执行bgrewriteaof命令手动重写AOF文件,那么等RDB快照生成之后,才会去执行AOF rewrite。

同时有RDB文件和AOF日志文件,那么redis重启的时候,会优先使用AOF进行数据恢复,因为其中的日志更完整。

四、redis 持久化机制的配置

######################### 通用 #########################
 
# 持久化文件(包括RDB文件和AOF文件)的存储目录,默认.
dir dir /home/hadoop/data/redis/6379
 
######################### RDB #########################
 
# RDB文件的文件名称,默认dump.rdb
dbfilename dump.rdb
 
# 生成RDB文件的策略,默认为以下3种,意思是:
# 每隔60s(1min),如果有超过10000个key发生了变化,就写一份新的RDB文件
# 每隔300s(5min),如果有超过10个key发生了变化,就写一份新的RDB文件
# 每隔900s(15min),如果有超过1个key发生了变化,就写一份新的RDB文件
# 配置多种策略可以同时生效,无论满足哪一种条件都会写一份新的RDB文件
save 900 1
save 300 10
save 60 10000
 
# 是否开启RDB文件压缩,该功能可以节约磁盘空间,默认为yes
rdbcompression yes
 
# 在写入文件和读取文件时是否开启rdb文件检查,检查是否有无损坏
# 如果在启动时检查发现文件损坏,则停止启动,默认yes
rdbchecksum yes
 
######################### AOF #########################
 
# 是否开启AOF机制,默认为no
appendonly yes
 
# AOF文件的名称,默认为appendonly.aof
appendfilename "appendonly.aof"
 
# fsync的策略,默认为everysec
# everysec:每秒fsync一次
# no:redis不主动fsync,完全交由操作系统决定
# always:1条指令fsync一次
appendfsync everysec
 
# AOF文件rewrite策略
# 当上一次重写后的AOF文件的增长比例达到100%
# 比如上一次重写AOF文件后,新文件大小为128M
# 当新文件再次增长了100%,达到了256M
# 并且增长了100%后的文件的大小大于64M,那么开始重写AOF文件
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
 
# 是否加载破损的AOF文件,默认为yes,如果设置为no
# 那么redis启动时如果发现AOF文件破损,就会报错并且拒绝启动redis服务。
aof-load-truncated yes
 
######################### 混合持久化 #########################
 
# 是否开启混合持久化机制,默认为no
aof-use-rdb-preamble no

五、其他相关命令

  • 手动生成新的RDB文件
# 阻塞主进程,直到生成新的RDB文件
save 

# 异步生成RDB文件,fork子进程去生成新的RDB文件,主进程不阻塞
bgsave
  • 手动重写AOF文件
bgrewriteaof
  • 停止redis服务
# 安全停止redis服务,在停止之前会生成一份新的RDB文件
redis-cli SHUTDOWN

# 不安全,会造成数据丢失
kill -9 redis_pid

  • 检查持久化文件
# 检查AOF文件
redis-check-aof /your/path/appendonly.aof

# 检查RDB文件
redis-check-rdb /your/path/dump.rdb

  • 修复AOF文件
#如果redis在append数据到AOF文件时,机器宕机了,可能会导致AOF文件破损,使用以下命令修复AOF文件
  • 查看持久化信息
# 查看持久化信息
info Persistence

# 查看状态信息
info stats

原文链接:https://blog.csdn.net/Seky_fei/article/details/106594029

最后编辑:
作者:摘星怪
这个作者貌似有点懒,什么都没有留下。

留下一个回复

你的email不会被公开。