Redis入门第五步:Redis持久化

欢迎继续跟随《Redis新手指南:从入门到精通》专栏的步伐!在本文中,我们将深入探讨Redis的持久化机制,这是确保数据在服务器重启后不会丢失的关键功能。了解如何配置和使用不同的持久化方法,对于构建可靠的应用程序至关重要。

在本文中,你将会学到:

  • 为什么需要持久化:理解数据持久化的重要性及其在实际应用中的作用。
  • RDB(快照)持久化
    • RDB的基本概念和工作原理。
    • 如何配置RDB持久化。
    • RDB的优点和缺点。
    • 实际案例分析:何时使用RDB。
  • AOF(Append Only File)持久化
    • AOF的基本概念和工作原理。
    • 如何配置AOF持久化。
    • AOF的优点和缺点。
    • 实际案例分析:何时使用AOF。
  • 混合持久化模式
    • 混合持久化的基本概念。
    • 混合持久化的优势。
    • 如何启用混合持久化。
  • 最佳实践
    • 性能优化技巧,例如调整RDB和AOF的频率。
    • 故障恢复策略,如备份和恢复数据。
    • 安全性和权限管理。

无论你是初学者还是有经验的开发者,本文都将为你提供清晰易懂的指导,帮助你在项目中有效利用Redis的持久化功能。让我们一起探索这一重要的数据保护机制吧!

1 Redis持久化介绍🍀​

为了防止数据丢失以及服务重启时能够恢复数据,Redis支持数据的持久化,主要分为两种方式,分别是RDB和AOF; 当然实际场景下还会使用这两种的混合模式

为什么需要持久化?

Redis是个基于内存的数据库。那服务一旦宕机,内存中的数据将全部丢失。通常的解决方案是从后端数据库恢复这些数据,但后端数据库有性能瓶颈,如果是大数据量的恢复:

  • 会对数据库带来巨大的压力

  • 数据库的性能不如Redis。导致程序响应慢

所以对Redis来说,实现数据的持久化,避免从后端数据库中恢复数据,是至关重要的

Redis 持久化方案

虽说 Redis 是内存数据库,但是它为数据的持久化提供了两种技术。分别是「RDB 快照和 AOF 日志 」

这两种技术都会用各用一个日志文件来记录信息,但是记录的内容是不同的

  • RDB 文件的内容是二进制数据

  • AOF 文件的内容是操作命令

总结:

  • 第一种:将当前数据状态进行保存,即快照形式,存储数据结果,存储格式简单,关注点在数据

  • 第二种:将数据的操作过程进行保存,即日志形式,存储操作过程,存储格式复杂,关注点在数据的操作过程

2 RDB持久化🍀

RDB 就是 Redis DataBase 的缩写,中文名为快照/内存快照,RDB持久化是把当前进程数据生成快照保存到磁盘上的过程,由于是某一时刻的快照,那么快照中的值要早于或者等于内存中的值。

 Redis 会单独创建(fork)一个子进程进行持久化,会先将数据写入一个临时文件中,待持久化过程结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程不进行任何 IO 操作,这就确保的极高的性能。如果需要大规模的数据的恢复,且对数据恢复的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加高效。RDB 唯一的缺点是最后一次持久化的数据可能会丢失。

触发RDB持久化的方式有2种,分别是手动触发自动触发

2.1 手动触发

Redis 提供了两个命令来生成 RDB 文件,分别是savebgsave,他们的区别就在于是否在「主线程」里执行:

  • 执行了 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程. 阻塞当前Redis服务器,知道RDB过程完成为止,对于内存较大的实例会造成长时间阻塞.线上环境不推荐使用

  • 执行了 bgsave 命令,Redis进程执行fork操作创建子进程,RDB持久胡过程由子进程负责,完成后自动结束.阻塞只发生在fork阶段,一半时间很短

RDB 文件的加载工作是在服务器启动时自动执行的,Redis 并没有提供专门用于加载 RDB 文件的命令

2.1.1 save 指令

# 手动执行一次保存操作
save

redis.conf 配置文件 save 指令相关:

# 设置持久化文件名,默认值为 dump.rdb,通常设置为 dump-端口号.rdb
dbfilename filename

# 设置存储.rdb文件的路径,通常设置成存储空间较大的目录中。如目录名称为 data
dir path

# 设置存储至本地数据库时是否压缩数据,默认 yes;若设置为 no 则节省 CPU 运行时间,但存储文件变大
rdbcompression yes|no

# 设置读写文件过程是否进行RDB格式校验,默认 yes;若设置为 no,节约读写 10% 时间消耗,单存在数据损坏的风险
rdbchecksum yes|no

工作原理

以上图为例,现在有四个客户端各自要执行一个指令,把这些指令发送到 redis 服务器后,他们的执行会有一个先后顺序问题,假定就是按照 1234 的顺序放过去的话,那会是什么样的呢?

Redis 是单线程的工作模式,它会创建一个任务队列,所有的命令都会进到这个队列里边,在这儿排队执行,执行完一个消失一个,当所有的命令都执行完了,OK,结果达到了。

但是如果现在我们执行 save 指令保存大数据量时,会是什么现象呢?

它会非常耗时,以至于它在执行时,后面的指令都要等,所以说这种模式是不友好的,这是 save 指令的一个问题,当 cpu 执行时会阻塞 redis 服务器,直到它执行完毕,所以这里不建议在线上环境用 save 指令

2.2.2 bgsave 指令

上问讲到了当 save 指令的数据量过大时,单线程执行方式造成效率过低,那应该如何处理呢?

此时我们可以使用 bgsave 指令,bg 其实是 background(后台执行)的意思。

执行命令:

# 手动启动后台保存操作,但不是立即执行
bgsave

redis.conf 配置文件 bgsave 指令相关:

# 后台存储过程中如果出现错误现象,是否停止保存操作,默认 yes
stop-writes-on-bgsave-error yes|no

# 其他
dbfilename filename
dir path
rdbcompression yes|no
rdbchecksum yes|no

工作原理

当执行 bgsave 时,客户端发出 bgsave 指令给到 redis 服务器。注意,这个时候服务器会马上回一个结果告诉客户端后台已经开始了,与此同时它会创建一个子进程,使用 Linux 的 fork 函数创建一个子进程,让这个子进程去执行 save 相关的操作。此时可以想一下,我们主进程一直在处理指令,而子进程在执行后台的保存,它会不会干扰到主进程的执行呢?

答案是不会,所以说它才是主流方案。子进程开始执行之后,它就会创建 RDB 文件把它存起来,操作完以后他会把这个结果返回,也就是说 bgsave 的过程分成两个过程:第一个是服务端拿到指令直接告诉客户端开始执行了;另一个过程是一个子进程在完成后台的保存操作,操作完以后回一个消息

具体流程如下

  • redis客户端执行bgsave命令或者自动触发bgsave命令;

  • 主进程判断当前是否已经存在正在执行的子进程,如果存在,那么主进程直接返回;

  • 如果不存在正在执行的子进程,那么就fork一个新的子进程进行持久化数据,fork过程是阻塞的,fork操作完成后主进程即可执行其他操作;

  • 子进程先将数据写入到临时的rdb文件中,待快照数据写入完成后再原子替换旧的rdb文件;

  • 同时发送信号给主进程,通知主进程rdb持久化完成,主进程更新相关的统计信息(info Persitence下的rdb_*相关选项)

2.2 自动触发

在以下4种情况时会自动触发:

  • redis.conf中配置save m n,即在m秒内有n次修改时,自动触发bgsave生成rdb文件;

  • 主从复制时,从节点要从主节点进行全量复制时也会触发bgsave操作,生成当时的快照发送到从节点;

  • 执行debug reload命令重新加载redis时也会触发bgsave操作;

  • 默认情况下执行shutdown命令时,如果没有开启aof持久化,那么也会触发bgsave操作;

redis.conf中配置RDB

快照周期:内存快照虽然可以通过技术人员手动执行SAVE或BGSAVE命令来进行,但生产环境下多数情况都会设置其周期性执行条件

Redis中默认的周期新设置

redis.conf
# 周期性执行条件的设置格式为
save <seconds> <changes>
​
# 默认的设置为:
save 900 1 # 如果900秒内有1条Key信息发生变化,则进行快照;
save 300 10 # 如果300秒内有10条Key信息发生变化,则进行快照;
save 60 10000 # 如果60秒内有10000条Key信息发生变化,则进行快照
​
# 以下设置方式为关闭RDB快照功能
save ""

其它相关配置

# 文件名称
dbfilename dump.rdb
# 文件保存路径
dir /home/work/app/redis/data/
# 如果持久化出错,主进程是否停止写入
stop-writes-on-bgsave-error yes
# 是否压缩
rdbcompression yes
# 导入时是否检查
rdbchecksum yes

2.3 RDB更深入理解

由于生产环境中我们为Redis开辟的内存区域都比较大(例如6GB),那么将内存中的数据同步到硬盘的过程可能就会持续比较长的时间,而实际情况是这段时间Redis服务一般都会收到数据写操作请求。那么如何保证数据一致性呢

RDB中的核心思路是Copy-on-Write,来保证在进行快照操作的这段时间,需要压缩写入磁盘上的数据在内存中不会发生变化。在正常的快照操作中,一方面Redis主进程会fork一个新的快照进程专门来做这个事情,这样保证了Redis服务不会停止对客户端包括写请求在内的任何响应。另一方面这段时间发生的数据变化会以副本的方式存放在另一个新的内存区域,待快照操作结束后才会同步到原来的内存区域

举个例子:如果主线程对这些数据也都是读操作(例如图中的键值对 A),那么,主线程和 bgsave 子进程相互不影响。但是,如果主线程要修改一块数据(例如图中的键值对 C),那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入 RDB 文件,而在这个过程中,主线程仍然可以直接修改原来的数据。

图片

在进行快照操作的这段时间,如果发生服务崩溃怎么办?

很简单,在没有将数据全部写入到磁盘前,这次快照操作都不算成功。如果出现了服务崩溃的情况,将以上一次完整的RDB快照文件作为恢复内存数据的参考。也就是说,在快照操作过程中不能影响上一次的备份数据。Redis服务会在磁盘上创建一个临时文件进行数据操作,待操作成功后才会用这个临时文件替换掉上一次的备份。

可以每秒做一次快照吗?

对于快照来说,所谓"连拍"就是指连续地做快照。这样一来,快照的间隔时间变得很短,即使某一时刻发生宕机了,因为上一时刻快照刚执行,丢失的数据也不会太多。但是,这其中的快照间隔时间就很关键了

如下图所示,我们先在 T0 时刻做了一次快照,然后又在 T0+t 时刻做了一次快照,在这期间,数据块 5 和 9 被修改了。如果在 t 这段时间内,机器宕机了,那么,只能按照 T0 时刻的快照进行恢复。此时,数据块 5 和 9 的修改值因为没有快照记录,就无法恢复了

图片

所以,要想尽可能恢复数据,t 值就要尽可能小,t 越小,就越像"连拍。那么,t 值可以小到什么程度呢,比如说是不是可以每秒做一次快照?毕竟,每次快照都是由 bgsave 子进程在后台执行,也不会阻塞主线程。

这种想法其实是错误的。虽然 bgsave 执行时不阻塞主线程,但是,如果频繁地执行全量快照,也会带来两方面的开销

  • 一方面,频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环

  • 另一方面,bgsave 子进程需要通过 fork 操作从主线程创建出来。虽然,子进程在创建后不会再阻塞主线程,但是,fork 这个创建过程本身会阻塞主线程,而且主线程的内存越大,阻塞时间越长。如果频繁 fork 出 bgsave 子进程,这就会频繁阻塞主线程

那么,有什么其他好方法吗?此时,我们可以做增量快照,就是指做了一次全量快照后,后续的快照只对修改的数据进行快照记录,这样可以避免每次全量快照的开销。这个比较好理解。

但是它需要我们使用额外的元数据信息去记录哪些数据被修改了,这会带来额外的空间开销问题。那么,还有什么方法既能利用 RDB 的快速恢复,又能以较小的开销做到尽量少丢数据呢?且看后文中4.0版本中引入的RDB和AOF的混合方式。

2.4 RDB优缺点

  • 优点
    1.RDB文件是某个时间节点的快照,默认使用LZF算法进行压缩,压缩后的文件体积远远小于内存大小,适用于备份、全量复制等场景

    2.Redis加载RDB文件恢复数据要远远快于AOF方式
  • 缺点
    1.RDB方式实时性不够,无法做到秒级的持久化

    2.每次调用bgsave都需要fork子进程,fork子进程属于重量级操作,频繁执行成本较高

    3.RDB文件是二进制的,没有可读性,AOF文件在了解其结构的情况下可以手动修改或者补全

    4.版本兼容RDB文件问题

3 AOF持久化🍀

Redis先执行命令,把数据写入内存,然后才记录日志。日志里记录的是Redis收到的每一条命令,这些命令是以文本形式保存

PS: 大多数的数据库采用的是写前日志(WAL),例如MySQL,通过写前日志和两阶段提交,实现数据和逻辑的一致性

AOF日志采用写方式记录,这意味着每当有写操作发生时,AOF文件会记录下操作的命令和参数。这些记录以文本形式存储在文件中,每个记录都以一个命令结尾

3.1 为什么采用写后日志

Redis要求高性能,采用写日志有两方面好处:

  • 避免额外的检查开销:Redis 在向 AOF 里面记录日志的时候,并不会先去对这些命令进行语法检查。所以,如果先记日志再执行命令的话,日志中就有可能记录了错误的命令,Redis 在使用日志恢复数据时,就可能会出错

  • 不会阻塞当前的写操作

但这种方式存在潜在风险:

  • 如果命令执行完成,写日志之前宕机了,会丢失数据

  • 主线程写磁盘压力大,导致写盘慢,阻塞后续操作

3.2 如何实现AOF

AOF日志记录Redis的每个写命令,步骤分为:命令追加(append)、文件写入(write)和文件同步(sync)。

  • 命令追加 当AOF持久化功能打开了,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区。

  • 文件写入和同步 关于何时将 aof_buf 缓冲区的内容写入AOF文件中,Redis提供了三种写回策略:

    配置项写回时机优点缺点
    Always同步写回可靠性高,数据基本不丢失每个写命令都要落盘,性能影响较大
    Everysec每秒写回性能适中宕机时丢失1秒内的数据
    No操作系统控制的写回性能好宕机时丢失的数据较多
    • Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘

    • Everysec,每秒写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘

    • No,操作系统控制的写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘

3.3 配置AOF

默认情况下,Redis是没有开启AOF的,可以通过配置redis.conf文件来开启AOF持久化,关于AOF的配置如下:

# appendonly参数开启AOF持久化
appendonly no # 默认情况下AOF功能是关闭的,将该选项改为yes以便打开Redis的AOF功能
# AOF持久化的文件名,默认是appendonly.aof
appendfilename "appendonly.aof" # 这个参数项很好理解了,就是AOF文件的名字
# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的
dir ./
# 同步策略
# appendfsync always
appendfsync everysec  # 这个参数项是AOF功能最重要的设置项之一,主要用于设置“真正执行”操作命令向AOF文件中同步的策略
# appendfsync no
# aof重写期间是否同步
no-appendfsync-on-rewrite no
# 重写触发配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 加载aof出错如何处理
aof-load-truncated yes
# 文件重写策略
aof-rewrite-incremental-fsync yes

什么叫"真正执行"呢?还记得Linux操作系统对磁盘设备的操作方式吗?为了保证操作系统中I/O队列的操作效率,应用程序提交的I/O操作请求一般是被放置在linux Page Cache中的,然后再由Linux操作系统中的策略自行决定正在写到磁盘上的时机。而Redis中有一个fsync()函数,可以将Page Cache中待写的数据真正写入到物理设备上,而缺点是频繁调用这个fsync()函数干预操作系统的既定策略,可能导致I/O卡顿的现象频繁 。

与上节对应,appendfsync参数项可以设置三个值,分别是:alwayseverysecno默认的值为everysec。

no-appendfsync-on-rewrite:always和everysec的设置会使真正的I/O操作高频度的出现,甚至会出现长时间的卡顿情况,这个问题出现在操作系统层面上,所有靠工作在操作系统之上的Redis是没法解决的。为了尽量缓解这个情况,Redis提供了这个设置项,保证在完成fsync函数调用时,不会将这段时间内发生的命令操作放入操作系统的Page Cache(这段时间Redis还在接受客户端的各种写操作命令)。

auto-aof-rewrite-percentage:上文说到在生产环境下,技术人员不可能随时随地使用"BGREWRITEAOF"命令去重写AOF文件。所以更多时候我们需要依靠Redis中对AOF文件的自动重写策略。Redis中对触发自动重写AOF文件的操作提供了两个设置:auto-aof-rewrite-percentage表示如果当前AOF文件的大小超过了上次重写后AOF文件的百分之多少后,就再次开始重写AOF文件。例如该参数值的默认设置值为100,意思就是如果AOF文件的大小超过上次AOF文件重写后的1倍,就启动重写操作。

auto-aof-rewrite-min-size:参考auto-aof-rewrite-percentage选项的介绍,auto-aof-rewrite-min-size设置项表示启动AOF文件重写操作的AOF文件最小大小。如果AOF文件大小低于这个值,则不会触发重写操作。注意,auto-aof-rewrite-percentage和auto-aof-rewrite-min-size只是用来控制Redis中自动对AOF文件进行重写的情况,如果是技术人员手动调用“BGREWRITEAOF”命令,则不受这两个限制条件左右

3.4 AOF优缺点

  • 优点

    • 数据安全性高:AOF文件记录了所有的写操作,因此即使在Redis服务器发生故障或崩溃的情况下,数据也不会丢失

    • 数据一致性强:由于AOF文件记录的是写操作的顺序,因此在Redis服务器启动时,它会按照这些写操作的顺序来重建数据库,从而保证数据的一致性

    • 性能较好:虽然AOF日志记录需要额外的磁盘I/O操作,但是相对于RDB方式,它不需要将整个数据库加载到内存中,因此对于大型数据库来说,AOF方式的性能相对较好

  • 缺点

    • AOF文件可能比RDB文件更大,因为它记录了所有的写操作

    • AOF文件恢复数据的时间可能比RDB文件更长,因为它需要执行所有的写操作来重建数据库

AOF 风险

当然,AOF 持久化功能也不是没有潜在风险。

  • 第一个风险,执行写操作命令和记录日志是两个过程,那当 Redis 在还没来得及将命令写入到硬盘时,服务器发生宕机了,这个数据就会有丢失的风险。

  • 第二个风险,前面说道,由于写操作命令执行成功后才记录到 AOF 日志,所以不会阻塞当前写操作命令的执行,但是可能会给「下一个」命令带来阻塞风险。
    因为将命令写入到日志的这个操作也是在主进程完成的(执行命令也是在主进程),也就是说这两个操作是同步的。

image

如果在将日志内容写入到硬盘时,服务器的硬盘的 I/O 压力太大,就会导致写硬盘的速度很慢,进而阻塞住了,也就会导致后续的命令无法执行。

认真分析一下,其实这两个风险都有一个共性,都跟「 AOF 日志写回硬盘的时机」有关。

3.5 AOF 三种回写策略

Redis 写入 AOF 日志的过程,如下图:

image

  1. Redis 执行完写操作命令后,会将命令追加到 server.aof_buf 缓冲区;

  2. 然后通过 write() 系统调用,将 aof_buf 缓冲区的数据写入到 AOF 文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区 page cache,等待内核将数据写入硬盘;

  3. 具体内核缓冲区的数据什么时候写入到硬盘,由内核决定。

Redis 提供了 3 种写回硬盘的策略,控制的就是上面说的第三步的过程。

在 redis.conf 配置文件中的appendfsync配置项可以有以下 3 种参数可填:

  • Always:这个单词的意思是「总是」,所以它的意思是每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘。

  • Everysec:这个单词的意思是「每秒」,所以它的意思是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;也是默认策略。

  • No:意味着不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。

这 3 种写回策略都无法能完美解决「主进程阻塞」和「减少数据丢失」的问题,因为两个问题是对立的,偏向于一边的话,就会要牺牲另外一边,原因如下:

  • Always 策略的话,可以最大程度保证数据不丢失,但是由于它每执行一条写操作命令就同步将 AOF 内容写回硬盘,所以是不可避免会影响主进程的性能。

  • No 策略的话,是交由操作系统来决定何时将 AOF 日志内容写回硬盘,相比于 Always 策略性能较好,但是操作系统写回硬盘的时机是不可预知的,如果 AOF 日志内容没有写回硬盘,一旦服务器宕机,就会丢失不定数量的数据。

  • Everysec 策略的话,是折中的一种方式,避免了 Always 策略的性能开销,也比 No 策略更能避免数据丢失,当然如果上一秒的写操作命令日志没有写回到硬盘,发生了宕机,这一秒内的数据自然也会丢失。

大家可以根据自己的业务场景进行选择:

  • 如果要高性能,就选择 No 策略。

  • 如果要高可靠,就选择 Always 策略。

  • 如果允许数据丢失一点,但又想性能高,就选择 Everysec 策略。

image

3.6 AOF 重写

AOF 重写介绍

场景:如果连续执行如下写数据的指令时,该如何处理?

image


随着命令不断写入 AOF,文件会越来越大,为了解决这个问题,Redis 引入了 AOF 重写机制压缩文件体积。AOF 文件重写是将 Redis 进程内的数据转化为写命令同步到新 AOF 文件的过程。简单说就是将对同一个数据的若干个条命令执行结果转化成最终结果数据对应的指令进行记录。

AOF重写作用:

  • 降低磁盘占用量,提高磁盘利用率。
  • 提高持久化效率,降低持久化写时间,提高 I/O 性能。
  • 降低数据恢复用时,提高数据恢复效率。

AOF重写规则:

  • 进程内具有时效性的数据,并且数据已超时将不再写入文件。

  • 非写入类的无效指令将被忽略,只保留最终数据的写入命令。

    • 如 del key1、 hdel key2、srem key3、set key4 111、set key4 222 等。

    • 如 select 指令虽然不更改数据,但是更改了数据的存储位置,此类命令同样需要记录。

  • 对同一数据的多条写命令合并为一条命令。

    • 如 lpush list1 a、lpush list1 b、lpush list1 c 可以转化为 lpush list1 a b c。
  • 为防止数据量过大造成客户端缓冲区溢出,对 list、set、hash、zset 等类型,每条指令最多写入 64 个元素。

AOF 重写配置

手动重写:

bgrewriteaof

手动重写原理分析:

image

自动重写:

auto-aof-rewrite-min-size size
auto-aof-rewrite-percentage percentage

自动重写触发比对参数(运行指令 info Persistence 获取具体信息):

aof_current_size  
aof_base_size

PROPERTIES 复制 全屏

自动重写触发条件公式:

image

3.7 深入理解AOF重写

AOF会记录每个写命令到AOF文件,随着时间越来越长,AOF文件会变得越来越大。如果不加以控制,会对Redis服务器,甚至对操作系统造成影响,而且AOF文件越大,数据恢复也越慢。为了解决AOF文件体积膨胀的问题,Redis提供AOF文件重写机制来对AOF文件进行"瘦身"

图例解释AOF重写

Redis通过创建一个新的AOF文件来替换现有的AOF,新旧两个AOF文件保存的数据相同,但新AOF文件没有了冗余命令

图片

AOF重写会阻塞吗?

AOF重写过程是由后台进程bgrewriteaof来完成的。主线程fork出后台的bgrewriteaof子进程,fork会把主线程的内存拷贝一份给bgrewriteaof子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志. 所以AOF在重写时,在fork进程时是会阻塞住主线程的

AOF日志何时会重写?

有两个配置项控制AOF重写的触发:

auto-aof-rewrite-min-size:表示运行AOF重写时文件的最小大小,默认为64MB。

auto-aof-rewrite-percentage:这个值的计算方式是,当前aof文件大小和上一次重写后aof文件大小的差值,再除以上一次重写后aof文件大小。也就是当前aof文件比上一次重写后aof文件的增量大小,和上一次重写后aof文件大小的比值

重写日志时,有新数据写入咋整?

重写过程总结为:"一个拷贝,两处日志"。在fork出子进程时的拷贝,以及在重写时,如果有新数据写入,主线程就会将命令记录到两个AOF日志内存缓冲区中。如果AOF写回策略配置的是always,则直接将命令写回旧的日志文件,并且保存一份命令至AOF重写缓冲区,这些操作对新的日志文件是不存在影响的。(旧的日志文件:主线程使用的日志文件,新的日志文件:bgrewriteaof进程使用的日志文件)

而在bgrewriteaof子进程完成会日志文件的重写操作后,会提示主线程已经完成重写操作,主线程会将AOF重写缓冲中的命令追加到新的日志文件后面。这时候在高并发的情况下,AOF重写缓冲区积累可能会很大,这样就会造成阻塞,Redis后来通过Linux管道技术让AOF重写期间就能同时进行回放,这样AOF重写结束后只需回放少量剩余的数据即可。

最后通过修改文件名的方式,保证文件切换的原子性。在AOF重写日志期间发生宕机的话,因为日志文件还没切换,所以恢复数据时,用的还是旧的日志文件

总结操作:

  • 主线程fork出子进程重写aof日志

  • 子进程重写日志完成后,主线程追加aof日志缓冲

  • 替换日志文件

温馨提示:这里的进程和线程的概念有点混乱。因为后台的bgreweiteaof进程就只有一个线程在操作,而主线程是Redis的操作进程,也是单独一个线程。这里想表达的是Redis主进程在fork出一个后台进程之后,后台进程的操作和主进程是没有任何关联的,也不会阻塞主线程S

4 混合持久化🍀

4.1 RDB 和 AOF 对比

持久化方式RDBAOF
占用存储空间小(数据级:压缩)大(指令级:重写)
存储速度
恢复速度
数据安全性会丢失数据依据策略决定
资源消耗高/重量级低/轻量级
启动优先级

4.2 RDB 和 AOF 合体

尽管 RDB 比 AOF 的数据恢复速度快,但是快照的频率不好把握:

  • 如果频率太低,两次快照间一旦服务器发生宕机,就可能会比较多的数据丢失;

  • 如果频率太高,频繁写入磁盘和创建子进程会带来额外的性能开销。

那有没有什么方法不仅有 RDB 恢复速度快的优点,又有 AOF 丢失数据少的优点呢?

当然有,那就是将 RDB 和 AOF 合体使用,这个方法是在 Redis 4.0 提出的,该方法叫混合使用 AOF 日志和内存快照,也叫混合持久化。

如果想要开启混合持久化功能,可以在 Redis 配置文件将下面这个配置项设置成 yes:

aof-use-rdb-preamble yes

混合持久化工作在 AOF 日志重写过程。当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。

也就是说,使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据

image

这样的好处在于:

  • 重启 Redis 加载数据的时候,由于前半部分是 RDB 内容,这样加载的时候速度会很快

  • 加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少地丢失

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值