3 Redis持久化机制

Redis持久化机制

redis是一个内存数据库,数据保存在内存中,但是我们都知道内存的数据变化是很快的,也容易发生丢失。幸好Redis还为我们提供了持久化的机制,分别是RDB(Redis DataBase)和AOF(Append Only File)。Redis持久化是为了快速的恢复数据而不是为了存储数据。

为什么要持久化

  • Redis是内存数据库,宕机后数据会消失。
  • Redis重启后快速恢复数据,要提供持久化机制
  • Redis持久化是为了快速的恢复数据而不是为了存储数据
  • Redis有两种持久化方式:RDB和AOF

注意:Redis持久化不保证数据的完整性。

当Redis用作DB时,DB数据要完整,所以一定要有一个完整的数据源(文件、mysql) 在系统启动时,从这个完整的数据源中将数据load到Redis中

数据量较小,不易改变,比如:字典库(xml、Table)

通过info命令可以查看关于持久化的信息

# Persistence
loading:0 
rdb_changes_since_last_save:1 
rdb_bgsave_in_progress:0 
rdb_last_save_time:1589363051 
rdb_last_bgsave_status:ok 
rdb_last_bgsave_time_sec:-1 
rdb_current_bgsave_time_sec:-1 
rdb_last_cow_size:0 aof_enabled:1 
aof_rewrite_in_progress:0 
aof_rewrite_scheduled:0 
aof_last_rewrite_time_sec:-1 
aof_current_rewrite_time_sec:-1 
aof_last_bgrewrite_status:ok 
aof_last_write_status:ok 
aof_last_cow_size:0 
aof_current_size:58 
aof_base_size:0 
aof_pending_rewrite:0 
aof_buffer_length:0 
aof_rewrite_buffer_length:0 
aof_pending_bio_fsync:0 
aof_delayed_fsync:0

 

Redis数据持久化流程

  • 客户端向服务端发送写操作(数据在客户端的内存中)
  • 数据库服务端接收到写请求的数据(数据在服务端的内存中)
  • 服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区)
  • 操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)
  • 磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)

RDB机制

RDB(Redis DataBase),是redis默认的存储方式,RDB方式是通过快照( snapshotting )完成的。RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。

触发快照的方式:

1. 符合自定义配置的快照规则

2. 执行save或者bgsave命令

3. 执行flushall命令

4. 执行主从复制操作 (第一次)

自定义配置快照规则

在redis.conf中配置:save    多少秒内    多少个key发生变化

下面是Redis的默认的快照保存配置

save "" # 不使用RDB存储 不能主从
save 900 1 # 表示15分钟(900秒钟)内至少1个键被更改则进行快照。 
save 300 10 # 表示5分钟(300秒)内至少10个键被更改则进行快照。 
save 60 10000 # 表示1分钟内至少10000个键被更改则进行快照。

①不需要持久化,那么你可以注释掉所有的 save 行来停用保存功能。

②stop-writes-on-bgsave-error :默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了

③rdbcompression ;默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。

④rdbchecksum :默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。

⑤dbfilename :设置快照的文件名,默认是 dump.rdb

⑥dir:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。

save触发方式

该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。

具体流程如下:

image.png

执行完成时候如果存在老的RDB文件,就把新的替代掉旧的。我们的客户端可能都是几万或者是几十万,这种方式显然不可取。

bgsave触发方式

127.0.0.1:6379> bgsave 

Background saving started

执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。

具体流程如下:

image.png

具体操作是Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。基本上 Redis 内部所有的RDB操作都是采用 bgsave 命令。

RDB执行流程(原理)

image.png

1. Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof(aof文件重写命令)的子 进程,如果在执行则bgsave命令直接返回。

2. 父进程执行fork(调用OS函数复制主进程)操作创建子进程,这个复制过程中父进程是阻塞的, Redis不能执行来自客户端的任何命令。

3. 父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令。

4. 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换。 (RDB始终完整)

5. 子进程发送信号给父进程表示完成,父进程更新统计信息。

6. 父进程fork子进程后,继续工作。

RDB文件结构

image.png

1、REDIS:头部5字节固定为“REDIS”字符串

2、RDB_VERSION:4字节“RDB”版本号(不是Redis版本号),当前为9,填充后为0009

3、AUX_FIELD_KEY_VALUE_PAIRS:辅助字段,以key-value的形式

4、DB_NUM:存储数据库号码

5、DB_DICT_SIZE:字典大小

6、EXPIRE_DICT_SIZE:过期key

7、KEY_VALUE_PAIRS:主要数据,以key-value的形式存储

8、EOF:结束标志

9、CHECK_SUM:校验和,就是看文件是否损坏,或者是否被修改。

RDB的优缺点

优点:

  • RDB是二进制压缩文件,占用空间小,便于传输(传给slaver)
  • RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。
  • 生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。(主进程不能太大,Redis的数据量不能过大)
  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

缺点:

  • 不保证数据完整性,会丢失最后一次快照以后更改的所有数据

AOF机制

  • AOF(append only file)是Redis的另一种持久化方式。Redis默认情况下是不开启的。
  • 开启AOF持久化后,Redis将所有对数据库进行过写入的命令(及其参数)(RESP)记录到 AOF 文件, 以此达到记录数据库状态的目的,这样当Redis重启后只要按顺序回放这些命令就会恢复到原始状态了。
  • AOF会记录过程,RDB只管结果

AOF持久化配置

配置redis.conf

# 可以通过修改redis.conf配置文件中的appendonly参数开启
appendonly yes

# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的。
dir ./

# 默认的文件名是appendonly.aof,可以通过appendfilename参数修改
appendfilename appendonly.aof

AOF原理

AOF文件中存储的是redis的命令,同步命令到AOF文件的整个过程可以分为三个阶段:

  1. 命令传播:Redis将执行完的命令、命令的参数、命令的参数个数等信息发送到AOF程序中。
  2. 缓存追加:AOF程序根据接收到的命令数据,将命令转换为网络通讯协议的格式,然后将协议内容追加到服务器的AOF缓存中。
  3. 文件写入和保存:AOF缓存中的内容被写入到AOF文件末尾,如果设定的AOF保存条件被满足的话,fsync函数或者fdatasync函数会被调用,将写入的内容真正地保存到磁盘中。

命令传播

当一个 Redis 客户端需要执行命令时,它通过网络连接,将协议文本发送给 Redis 服务器。服务器在接到客户端的请求之后,它会根据协议文本的内容, 选择适当的命令函数, 并将各个参数从字符串文本转换为 Redis 字符串对象( StringObject )。每当命令函数成功执行之后, 命令参数都会被传播到 AOF 程序。

缓存追加

当命令被传播到 AOF 程序之后, 程序会根据命令以及命令的参数,将命令从字符串对象转换回原来的协议文本。协议文本生成之后,它会被追加到 redis.h/redisServer 结构的 aof_buf 末尾。

redisServer 结构维持着 Redis 服务器的状态,aof_buf 域则保存着所有等待写入到 AOF 文件的协议文本(RESP)。

文件写入和保存

每当服务器常规任务函数被执行、或者事件处理器被执行时,aof.c/flushAppendOnlyFile 函数都会被调用,这个函数执行以下两个工作:

WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件。

SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。

AOF保存模式

Redis目前支持三种AOF保存模式,它们分别是:

AOF_FSYNC_NO :不保存。

AOF_FSYNC_EVERYSEC :每一秒钟保存一次。(默认)

AOF_FSYNC_ALWAYS :每执行一个命令保存一次。(不推荐)

以下将分别讨论这三种保存模式。

不保存

在这种模式下,每次调用 flushAppendOnlyFile 函数, WRITE 都会被执行,但 SAVE 会被略过。

在这种模式下, SAVE 只会在以下任意一种情况中被执行:

①Redis被关闭 ②AOF功能被关闭 ③系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执 行)

这三种情况下的 SAVE 操作都会引起 Redis 主进程阻塞。

每一秒钟保存一次(推荐)

在这种模式中,SAVE 原则上每隔一秒钟就会执行一次,因为 SAVE 操作是由后台子线程(fork)调用的,所以它不会引起服务器主进程阻塞。

每执行一个命令保存一次

在这种模式下,每次执行完一个命令之后, WRITE 和 SAVE 都会被执行。

另外,因为 SAVE 是由 Redis 主进程执行的,所以在 SAVE 执行期间,主进程会被阻塞,不能接受命令请求。

AOF保存模式对性能和安全性的影响

对于三种 AOF 保存模式,它们对服务器主进程的阻塞情况如下:

image.png

AOF重写

AOF记录数据的变化过程,越来越大,需要重写“瘦身”

Redis可以在AOF体积变得过大时,自动地在后台(Fork子进程)对AOF进行重写。重写后的新 AOF文件包含了恢复当前数据集所需的最小命令集合。 所谓的“重写”其实是一个有歧义的词语,实际上,AOF重写并不需要对原有的AOF文件进行任何写入和读取,它针对的是数据库中键的当前值。

举例如下:

set s1 11
set s1 22
set s1 33
没有优化的:
set s1 11
set s1 22
set s1 33
优化后:
set s1 33
 
lpush list1 1 2 3
lpush list1 4 5 6
优化后:
lpush list1 1 2 3 4 5 6

Redis不希望AOF重写造成服务器无法处理请求,所以Redis决定将AOF重写程序放到(后台)子进程里执行,这样处理的最大好处是:

1、子进程进行AOF重写期间,主进程可以继续处理命令请求。

2、子进程带有主进程的数据副本,使用子进程而不是线程,可以在避免锁的情况下,保证数据的安全性。

不过,使用子进程也有一个问题需要解决: 因为子进程在进行AOF重写期间,主进程还需要继续处理命令,而新的命令可能对现有的数据进行修改,这会让当前数据库的数据和重写后的AOF文件中的数 据不一致。

为了解决这个问题,Redis增加了一个AOF重写缓存,这个缓存在fork出子进程之后开始启用,Redis主进程在接到新的写命令之后,除了会将这个写命令的协议内容追加到现有的AOF文件之外,还会追加到这个缓存中。

image.png

重写过程分析(整个重写操作是绝对安全的):

Redis在创建新AOF文件的过程中,会继续将命令追加到现有的AOF文件里面,即使重写过程中发生停机,现有的AOF文件也不会丢失。而一旦新AOF 文件创建完毕,Redis就会从旧AOF文件切换到新AOF文件,并开始对新AOF文件进行追加操作。

当子进程在执行AOF重写时,主进程需要执行以下三个工作:

  1. 处理命令请求。
  2. 将写命令追加到现有的AOF文件中。
  3. 将写命令追加到AOF重写缓存中。

这样一来可以保证:

  • 现有的AOF功能会继续执行,即使在AOF重写期间发生停机,也不会有任何数据丢失。
  • 所有对数据库进行修改的命令都会被记录到AOF重写缓存中。

当子进程完成AOF重写之后,它会向父进程发送一个完成信号,父进程在接到完成信号之后,会调用一个信号处理函数,并完成以下工作:

将AOF重写缓存中的内容全部写入到新AOF文件中。对新的AOF文件进行改名,覆盖原有的AOF文件。

Redis数据库里的+AOF重写过程中的命令-->新的AOF文件-->覆盖老的

当步骤 1 执行完毕之后,现有AOF文件、新AOF文件和数据库三者的状态就完全一致了。

当步骤 2 执行完毕之后,程序就完成了新旧两个AOF文件的交替。

这个信号处理函数执行完毕之后,主进程就可以继续像往常一样接受命令请求了。

在整个AOF后台重写过程中,只有最后的写入缓存和改名操作会造成主进程阻塞,在其他时候,AOF后台重写都不会对主进程造成阻塞,这将AOF重写对性能造成的影响降到了最低。

以上就是AOF后台重写,也即是BGREWRITEAOF命令(AOF重写)的工作原理。

触发方式

在redis.conf文件中配置触发方式

# 表示当前aof文件大小超过上一次aof文件大小的百分之多少的时候会进行重写。如果之前没有重写过,以启动时aof文件大小为准
auto-aof-rewrite-percentage 100
# 限制允许重写最小aof文件大小,也就是文件大小小于64mb的时候,不需要进行优化
auto-aof-rewrite-min-size 64mb

执行bgrewriteaof命令

127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started

混合持久化

RDB和AOF各有优缺点,Redis 4.0开始支持rdb和aof的混合持久化。如果把混合持久化打开,aof rewrite的时候就直接把rdb的内容写到aof文件开头。

RDB的头+AOF的身体=appendonly.aof

开启混合持久化

aof-use-rdb-preamble yes

image.png

我们可以看到该AOF文件是rdb文件的头和aof格式的内容,在加载时,首先会识别AOF文件是否以REDIS字符串开头,如果是就按RDB格式加载,加载完RDB后继续按AOF格式加载剩余部分。

AOF文件的载入与数据还原

因为AOF文件里面包含了重建数据库状态所需的所有写命令,所以服务器只要读入并重新执行一遍AOF文件里面保存的写命令,就可以还原服务器关闭之前的数据库状态。

Redis读取AOF文件并还原数据库状态的详细步骤如下:

1、创建一个不带网络连接的伪客户端(fake client):因为Redis的命令只能在客户端上下文中执行,而载入AOF文件时所使用的命令直接来源于AOF文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端来执行AOF文件保存的写命令,伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样

2、从AOF文件中分析并读取出一条写命令

3、使用伪客户端执行被读出的写命令

4、一直执行步骤2和步骤3,直到AOF文件中的所有写命令都被处理完毕为止 当完成以上步骤之后,AOF文件所保存的数据库状态就会被完整地还原出来。

整个过程如下图所示:

image.png

RDB与AOF对比

1、RDB存某个时刻的数据快照,采用二进制压缩存储,AOF存操作命令,采用文本存储(混合)

2、RDB性能高、AOF性能较低

3、RDB在配置触发状态会丢失最后一次快照以后更改的所有数据,AOF设置为每秒保存一次,则最多丢2秒的数据

4、Redis以主服务器模式运行,RDB不会保存过期键值对数据,Redis以从服务器模式运行,RDB会保存过期键值对,当主服务器向从服务器同步时,再清空过期键值对。AOF写入文件时,对过期的key会追加一条del命令,当执行AOF重写时,会忽略过期key和del命令。

应用场景

  • 作为内存数据库使用时,rdb+aof方式数据不容易丢
  • 有原始数据源时,每次启动时都从原始数据源中初始化,则不用开启持久化(数据量较小)
  • 作为缓存服务器时,使用rdb方式,性能高
  • 在数据还原时:有rdb+aof则还原aof,因为RDB会造成文件的丢失,AOF相对数据要完整。只有rdb,则还原rdb

配置策略

  • 追求高性能:都不开Redis宕机从数据源恢复
  • 字典库:不驱逐,保证数据完整性,不开持久化
  • 用作DB:不能主从,数据量小
  • 做缓存 较高性能:开rdb,
  •   Redis数据量存储过大,性能突然下降(fork时间过长,阻塞主进程)则只开AOF

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值