当前位置:网站首页>redis AOF持久化个人理解
redis AOF持久化个人理解
2022-08-03 07:02:00 【零舍】
AOF概念:
AOF(Append Only File)以文本的形式(文本格式由Redis自定义,后文会讲到),通过将所有对数据库的写入命令记录到AOF文件中,达到记录数据库状态的目的。
注意:AOF文件只会记录Redis的写操作命令,因为读命令对数据的恢复没有任何意义。
AOF使用:
Redis默认并未开启AOF功能,redis.conf配置文件中,关于AOF的相关配置如下:
# 是否开启AOF功能(开启:yes 关闭:no)
appendonly yes
# 生成的AOF文件名称
appendfilename appendonly.aof
# AOF写回策略
appendfsync everysec
# 当前AOF文件大小和最后一次重写后的大小之间的比率>=指定的增长百分比则进行重写
# 如100代表当前AOF文件大小是上次重写的两倍时候才重写
auto-aof-rewrite-percentage 100
# AOF文件最小重写大小,只有当AOF文件大小大于该值时候才可能重写,4.0默认配置64mb。
auto-aof-rewrite-min-size 64mb
AOF工作流程:
AOF的工作流程操作:命令写入(文件写入)(append)、文件同步(sync)、文件重写(rewrite)、重启加载(load)。
命令写入(文件写入):
Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF程序中,AOF程序再根据协议格式追加到Redis的aof_buf缓冲区,所以aof_buf 缓冲区保存着所有等待写入到AOF 文件的协议文本。
注意:Redis成功执行命令后,才会把命令写入AOF缓冲区,最后写入AOF文件
为什么要后记录日志呢,原因如下:
1、当命令可以成功执行,表明命令是无误的,所以可以避免对指令进行语法检查,避免出现记录错误指令的情况。
2、先执行命令后保存日志,不会阻塞当前的写操作。
AOF 的潜在风险:
1、如果命令执行成功,写入日志的时候宕机了,命令没有写入到日志中,这时候就有丢失数据的风险了,因为这时候没有写入日志,服务断电之后,这部分数据就丢失了。
2、AOF 的日志写入也是在主线程进行的,如果磁盘的压力很大,写入速度变慢了,会影响后续的操作。
这两种情况可通过调整 AOF 文件的写入磁盘的时机来避免
文件同步:
在AOF功能开启的情况下,文件事件会将成功执行之后的写命令追加到aof_buf缓冲区,在主服务进程死循环的最后,会调用flushAppendOnlyFile函数,该函数会将aof_buf中的数据写入到内核缓冲区,然后判断是否应该进行同步。
redis.conf配置文件中appendOnlyFile的选项有三个值可选,对应三种AOF同步策略,分别是:
no
命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步,同步硬盘操作由操作系统负责,通常同步周期最长30秒everysec
命令写入aof_buf后调用系统write操作,write完成线程返回,fsnc同步文件操作由专门线程每秒调用一次always
命令写入aof_buf后调用系统fsync操作同步到AOF文件,fsync完成后线程返回
以下三个小节将分别讨论这三种保存模式 :
No
由操作系统内核决定同步时机,每个写命令执行完,只是先把日志写入AOF文件的内核缓冲区,不立即进行同步。在这种模式下, 同步只会在以下任意一种情况下被执行:
- Redis 被关闭
- AOF 功能被关闭
- 系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行)
这三种情况下的同步操作都会引起 Redis 主进程阻塞。
Everysec
如果用户未指定appendOnlyFile的值,则默认值为Everysec。每秒同步,每个写命令执行完,只是先把日志写到 AOF文件的内核缓冲区,理论上每隔1秒把缓冲区中的内容同步到磁盘,且同步操作有单独的子线程进行,因此不会阻塞主进程。
需要注意的是,我们用的是「理论上」这样的措辞,实际运行中该模式对fsync或fdatasync的调用并不是每秒一次,而是和调用flushAppendOnlyFile函数时Redis所处的状态有关。
每当 flushAppendOnlyFile 函数被调用时, 可能会出现以下四种情况:
子线程正在执行 SAVE ,并且:
1、这个 SAVE 的执行时间未超过 2 秒,那么程序直接返回,并不执行 WRITE 或新的 2、SAVE 这个 SAVE 已经执行超过 2 秒,那么程序执行 WRITE ,但不执行新的 SAVE ,注意,因为这时 WRITE 的写入必须等待子线程先完成(旧的) SAVE ,因此这里 WRITE 会比平时阻塞更长时间。子线程没有在执行 SAVE ,并且:
1、上次成功执行 SAVE 距今不超过 1 秒,那么程序执行 WRITE ,但不执行 SAVE 2、上次成功执行 SAVE 距今已经超过 1 秒,那么程序执行 WRITE 和 SAVE 。
可以用流程图表示这四种情况:
在Everysec模式下,如果在情况1下宕机,那么我们最多损失小于2秒内的所有数据。如果在情况2下宕机,那么我们损失的数据可能会超过2秒(虽然发生的可能性很小)。因此AOF在Everysec模式下只会丢失 1 秒钟数据的说法实际上并不准确。
Always
每个写命令执行完,立刻同步地将日志写回磁盘。此模式下同步操作是由 Redis 主进程执行的,所以在同步执行期间,主进程会被阻塞,不能接受命令请求。
AOF同步策略对性能和安全性的影响
对于三种 AOF 同步模式, 它们对Redis主进程的阻塞情况如下:
- 不同步(No):写入和同步都由主进程执行,两个操作都会阻塞主进程;
- 每一秒钟同步一次(Everysec):写入操作由主进程执行,阻塞主进程。同步操作由子线程执行,不直接阻塞主进程,但同步操作完成的快慢会影响写入操作的阻塞时长;
- 每执行一个命令同步一次(Always):同模式 1 。
因为阻塞操作会让 Redis 主进程无法持续处理请求, 所以一般说来, 阻塞操作执行得越少、完成得越快, Redis 的性能就越好。
No的同步操作只会在AOF关闭或Redis关闭时执行, 或由操作系统内核触发。在一般情况下, 这种模式只需要为写入阻塞,因此它的写入性能要比后面两种模式要高, 但是这种性能的提高是以降低安全性为代价的:在这种模式下,如果发生宕机,那么丢失的数据量由操作系统内核的缓存冲洗策略决定。
Everysec在性能方面要优于Always , 并且在通常情况下,这种模式最多丢失不多于2秒的数据, 所以它的安全性要高于No ,这是一种兼顾性能和安全性的保存方案。
Always的安全性是最高的,但性能也是最差的,因为Redis必须阻塞直到命令信息被写入并同步到磁盘之后才能继续处理请求。
三种 AOF模式的特性可以总结为如下表格:
也就是说,通常情况下,当使用AOF进行同步时,子线程如果正在进行同步,那么主线程的写入操作也会被阻塞,此时主线程将根据情况对这个写入操作进行适当的处理,如果子线程执行时间未超过 2 秒,那么主线程直接返回,去处理其他操作,超过两秒,主线程将堵塞,等待同步完成将写操作写入缓冲区。
AOF生成过程小结:
- Redis成功执行写操作指令,然后将写的指令按照自定义格式追加到aof_buf缓冲区,这是第一个缓冲区;
- Redis主进程将aof_buf缓冲区的数据写入到内核缓冲区,这是第二个缓冲区;
- 根据AOF同步策略适时地将内核缓冲区的数据同步到磁盘,过程结束。
AOF重写 :
原因:
AOF的作用是帮我们还原Redis的数据状态,其中包含了所有的写操作,但是正常情况下客户端会对同一个KEY进行多次不同的写操作,如下:
127.0.0.1:6379[3]> SET name chanmufeng1
OK
127.0.0.1:6379[3]> SET name chanmufeng2
OK
127.0.0.1:6379[3]> SET name chanmufeng3
OK
127.0.0.1:6379[3]> SET name chanmufeng4
OK
127.0.0.1:6379[3]> SET name chanmufeng
OK
例子中对name的数据进行写操作就进行了5次,其实对我们而言仅需要最后一条指令而已,但是AOF会将这5条指令都记录下来。更极端的情况是有些被频繁操作的键, 对它们所调用的命令可能有成百上千、甚至上万条, 如果这样被频繁操作的键有很多的话,AOF文件的体积就会急速膨胀。
- 首先,AOF文件的体积受操作系统大小的限制,本身就不能无限增长;
- 其次,体积过于庞大的AOF文件会影响指令的写入速度,阻塞时间延长;
- 最后AOF文件的体积越大,Redis数据恢复所需的时间也就越长。
为了解决AOF文件体积庞大的问题,Redis提供了rewrite的AOF重写功能来精简AOF文件体积。
AOF重写过程触发条件:
- 手动触发:直接调用bgrewriteaof命令。
- 自动触发:根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机
auto-aof-rewrite-min-size:表示运行AOF重写时文件最小体积,默认为64MB。
auto-aof-rewrite-percentage:代表当前AOF文件空间(aof_current_size)和上一次重写后 AOF文件空间(aof_base_size)的比值。
每次当Redis中的定时函数 serverCron 执行时, 它都会检查以下条件是否全部满足, 如果是的话, 就会触发自动的 AOF 重写:
- 没有 bgsave 命令在进行。
- 没有 bgrewriteaof 在进行。
- 当前 AOF 文件大小大于 我们设置的auto-aof-rewrite-min-size。
- 当前 AOF 文件大小和最后一次 AOF 重写后的大小之间的比率大于等于指定的增长百分比auto-aof-rewrite-percentage。
默认情况下, 增长百分比为 100% , 也即是说, 如果前面三个条件都已经满足, 并且当前 AOF 文件大小比最后一次 AOF 重写时的大小要大一倍的话, 那么触发自动 AOF 重写。
重写实现:
为了避免阻塞主线程,导致数据库性能下降,和 AOF 日志由主进程写入不同,重写过程是由主进程fork一个子进程执行bgrewriteaof 来完成的。这样处理的最大好处是:
- 子进程进行 AOF重写期间,主进程可以继续处理命令请求;
- 子进程带有主进程的数据副本,操作效率更高。
当子进程在执行 AOF 重写时, 主进程需要执行以下三个工作:
- 处理命令请求。
- 将写命令追加到现有的 AOF 文件中。
- 将写命令追加到 AOF 重写缓存中。
这样使用子进程也有一个问题需要解决: 因为子进程在进行 AOF 重写期间, 主进程还需要继续处理命令, 而新的命令可能对现有的数据进行修改, 这会让当前数据库的数据和重写后的 AOF 文件中的数据不一致。
为了解决这个问题, Redis 增加了一个 AOF 重写缓存, 这个缓存在 fork 出子进程之后开始启用, Redis 主进程在接到新的写命令之后, 除了会将这个写命令的协议内容追加到AOF缓冲区,再写入现有的 AOF 文件之外, 还会追加到AOF重写缓存中,这样就可以保证:
- 现有的 AOF功能会继续执行,即使在 AOF 重写期间发生停机,也不会有任何数据丢失;
- 所有对数据库进行修改的命令都会被记录到AOF重写缓冲区中。
当子进程完成 AOF重写之后, 它会向父进程发送一个完成信号, 父进程在接到完成信号之后, 会调用一个信号处理函数, 并完成以下工作:
- 将 AOF重写缓冲区中的内容全部写入到新AOF 文件中;
- 对新的 AOF 文件进行改名,覆盖原有的 AOF 文件。注意,这是一个原子操作,改名过程中不接受客户端指令。
此时AOF缓冲区和AOF重写缓冲区在这段时间内保存的命令一致,并且主进程完成上述工作,会堵塞其他命令,所以能保证数据一致。
当步骤 1 执行完毕之后, 现有 AOF 文件、新 AOF 文件和数据库三者的状态就完全一致了。
当步骤 2 执行完毕之后, 程序就完成了新旧两个 AOF 文件的交替。
这个信号处理函数执行完毕之后, 主进程就可以继续像往常一样接受命令请求了。 在整个 AOF 后台重写过程中, 只有将AOF重写缓冲区数据写入新AOF文件和改名操作会造成主进程阻塞, 其他时候, AOF 后台重写都不会对主进程造成阻塞, 这将 AOF 重写对性能造成的影响降到了最低。
重写流程如下:
重启加载:
当Redis服务器重启时,可以加载AOF文件进行数据恢复。
Redis 读取 AOF 文件并且还原数据库状态的详细步骤如下:
创建一个不带网络连接的的伪客户端( fake client),因为 Redis 的命令只能在客户端上下文中执行,而载入 AOF 文件时所使用的的命令直接来源于 AOF 文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端来执行 AOF 文件保存的写命令,伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样的。
从 AOF 文件中分析并取出一条写命令。
使用伪客户端执行被读出的写命令。
一直执行步骤 2 和步骤3,直到 AOF 文件中的所有写命令都被处理完毕为止。
当完成以上步骤之后,AOF 文件所保存的数据库状态就会被完整还原出来。
文件校验:
对于错误格式的AOF文件:先进行备份,然后采用redis-check-aof --fix命令进行修复,修复后使用diff -u对比数据的差异,找出丢失的数据。
AOF文件结尾不完整的情况下:可以使用aof-load-truncated配置来兼容这种情况。
小结:
AOF是将Redis的所有写日志同步到磁盘的一种持久化方法,通过执行AOF中记录的所有指令可以达到恢复Redis原始数据状态的目的。
对于指令的同步时机,Redis提供了三种AOF同步策略,分别是No,Everysec,Always,三种策略对Redis性能的负面影响是由低到高的,在数据可靠性上也是由低到高的。
为了解决AOF日志太大的问题,Redis提供了AOF重写的机制,利用「写时复制」和「AOF重写缓冲区」达到精简AOF文件的目的。
边栏推荐
猜你喜欢
随机推荐
帆软11版本参数联动为null查询全部
华为设备配置BFD与接口联动(触发与BFD联动的接口物理状态变为Down)
static数据成员
模型训练前后显卡占用对比、多卡训练GPU占用分析【一文读懂】
(十四)51单片机——LCD1602实现滚动效果
Roson的Qt之旅#103 QML之标签导航控件TabBar
keepalived安装部署
海思项目总结
数仓埋点体系与归因实践
【着色器实现Glow可控局部发光效果_Shader效果第十三篇】
JS 原型原型链
STL迭代器
JS 预编译
pt-online-schema-change工具使用的一次
Postman will return to results generated CSV file to the local interface
ORB-SLAM2提取特征点
第六章:存储系统
【图像去噪】基于matlab稀疏表示KSVD图像去噪【含Matlab源码 2016期】
【图像边缘检测】基于matlab灰度图像的积累加权边缘检测【含Matlab源码 2010期】
死锁的成因和对应的解决方案