Redis 持久化方式 AOF 技术原理?一文带你从底层彻底吃透
AOF 命令写入的内容直接是文本协议格式。AOF 文件是纯文本文件,其内容正是 Redis 客户端向 Redis 发送的原始通信协议的内容。
例如:set hello world 这条命令,在 AOF 缓冲区会追加如下文本:
Bash
$3
set
$5
hello
$5
world
$3 : set 指令的长度。set 长度 == 3
set:代表着 set 指令
$5:hello key 对应的值长度
hello:key 值
$5:world value 对应的值长度
world:value 值
AOF 为什么直接采用文本协议格式?可能的理由如下:文本协议具有很好的兼容性。
开启 AOF 后,所有写入命令都包含追加操作,直接采用协议格式,避免二次处理开销。文本协议具有可读性,方便直接修改和处理。
AOF 为什么把命令追加到 aof_buf 中?Redis 使用单线程响应命令,如果每次写 AOF 文件命令都直接追加到硬盘,那么性能完全取决于当前硬盘负载。先写入缓冲区 aof_buf 中,还有另一个好处,Redis 可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。
AOF 配置
=====
在 redis 的 redis.conf 配置文件中:
=======================
打开文件,找到 APPEND ONLY MODE 对应内容,默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,通过 appendonly 参数开启:
AOF 文件的保存位置和 RDB 文件的位置相同
都是通过 dir 参数设置的
dir /path
redis 默认关闭,开启需要手动把 no 改为 yes
appendonly yes
指定本地数据库文件名,默认值为 appendonly.aof
Bash
appendfilename "appendonly.aof"
Redis 支持三种不同的刷写模式,Redis 提供了多种 AOF 缓冲区同步文件策略,由参数 appendfsync 控制:每次有数据修改发生时都写入 AOF 文件中每次收到写命令就立即强制写入磁盘,是最有保证的完全的持久化,但速度也是最慢的,一般不推荐使用 appendfsync always?**在一般的 STAT 硬盘上,Redis 只能支持大约几百 TPS 写入,这是最安全也是最慢的方式,显然跟 Redis 高性能特性背道而驰,不建议配置。**每秒中同步一次,将过去一秒内发生的数据修改写入 AOF 文件中每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,是受推荐的方式。appendfsync everysec?**是建议的同步策略,也是默认配置,做到兼顾性能和数据安全性,理论上只有在系统突然宕机的情况下丢失 1s 的数据。(严格来说最多丢失 1s 数据是不准确)**不主动同步,高效但是数据不持久化,由操作系统来决定完全依赖 OS 的写入,一般为 30 秒左右一次,性能最好但是持久化最没有保证,不被推荐。appendfsync no 由于操作系统每次同步 AOF 文件的周期,(即每 30 秒一次),而且会极大每次同步硬盘的数据量,虽然提升了性能,但数据安全性无法保证。
Redis 服务的 AOF 文件同步策略:always?(最安全但是最慢)?:同步持久化,每次发生数据变化会立刻写入到磁盘中。性能较差当数据完整性比较好(慢,安全)everysec?(默认的同步策略):出厂默认推荐,每秒异步记录一次(默认值)no?(最快但是不安全):不同步
当进程中 BGSAVE 或 BGREWRITEAOF 命令正在执行时不阻止主进程中的 fsync()调用(默认为 no,当存在延迟问题时需调整为 yes).
no-appendfsync-on-rewrite no
虽然每次执行更改数据库内容的操作时,AOF 都会将命令记录在 AOF 文件中,但是事实上,由于操作系统的缓存机制,数据并没有真正地写入硬盘,而是进入了系统的硬盘缓存。在默认情况下系统每 30 秒会执行一次同步操作,以便将硬盘缓存中的内容真正地 写入硬盘,在这 30 秒的过程中如果系统异常退出则会导致硬盘缓存中的数据丢失。一般来讲启用 AOF 持久化的应用都无法容忍这样的损失,这就需要 Redis 在写入 AOF 文件后主动要求系统将缓存内容同步到硬盘中。
AOF 重写机制
=======
随着命令不断写入 AOF,文件会越来越大,为了解决这个问题,Redis 引入了 AOF 重写机制压缩文件体积。AOF 文件重写是把 Redis 进程内的数据转化为写命令同步到新 AOF 文件的过程。
AOF 重写原理
=======
重写后的 AOF 文件为什么可以变小?有如下原因:
进程内已经超时的数据不再写文件。
旧的 AOF 文件含有无效命令,如 del key1、set a 111、set a 222 等。重写使用进程内数据直接生成,这样新的 AOF 文件只保留最终数据的写入命令。
多条写命令可以合并为一个,如 lpush list a、lpush list b、 lpush list c 可以转化为:lpush list a b c。
为了防止合并的数据过大造成客户端缓冲区溢出,对于 list、set、hash、zset 等类型,以 64 个元素为界拆分为多条。
AOF 重写原理
=======
AOF 的工作原理是将写操作追加到文件中,文件的冗余内容会越来越多。所以聪明的 Redis 新增了重写机制。当 AOF 文件的大小超过所设定的阈值时,Redis 就会对 AOF 文件的内容压缩。
Redis 会 fork 出一条新进程,读取内存中的数据,并重新写到一个临时文件中。并没有读取旧文件。最后替换旧的 aof 文件。
需要压缩重写的案例:
==========
AOF 带来了另一个问题,持久化文件会变得越来越大。比如,我们调用 INCR test 命令 100 次,文件中就必须保存全部的 100 条命令,但其实 99 条都是多余的。因为要恢复数据库的状态其实文件中保存一条 SET test 100 就够了。
为了合并重写 AOF 的持久化文件,Redis 提供了 bgrewriteaof 命令。收到此命令后,Redis 将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的文件,以此来实现控制 AOF 文件的合并重写(会将重写过程中接收的的新的指令和生成新的重写后 AOF 文件中的指令进行合并)。
注意:由于是模拟快照的过程,因此在重写 AOF 文件时并没有读取旧的 AOF 文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的 AOF 文件
AOF 重写目的
=======
AOF 重写降低了文件占用空间,除此之外,另一个目的是:更小的 AOF 文件可以更快地被 Redis 加载。
AOF 重写过程可以手动触发和自动触发:
===================
配置重写 rewrite 触发机制
手动触发:直接调用 bgrewriteaof 命令
自动触发:根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数确定自动触发时机
a
uto-aof-rewrite-min-size:?限制了允许重写的最小 AOF 文件,通常在 AOF 文件很小的时候即使其中有些冗余命令也可是可以忽略的。
auto-aof-rewrite-percentage:?当前的 AOF 文件大小超过上一次重写的 AOF 文件大小的百分之多少时会再次进行重写,如果之前没有重写过,则以启动时的 AOF 大小为依据。
注意,执行 AOF 重写请求时,父进程依然响应命令,Redis 使用"AOF 重写缓冲区"保存这部分新数据,防止新 AOF 文件生成期间丢失这部分数据。
CSS
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
解释含义:当 AOF 文件大小是上次 rewrite 后大小的一倍且文件大于 64M 时触发。一般都设置为 3G,64M 太小了,?这里的“一倍”和“64M” 可以通过配置文件修改。
AOF 与 RDB 二者选择的标准(结合上一篇文章)
=======================
权衡的标准是对数据的一致性要求和性能之间的平衡。看系统是愿意牺牲一些性能,换取更高的缓存一致性(AOF),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行 save 的时候,再做备份(RDB)。
AOF 的执行流程
========
开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入硬盘中的 AOF 文件。
AOF 的工作流程操作有:
============
命令写入(append)
文件同步(sync)
文件重写(rewrite)
重启加载(load)
AOF 流程如下:
========
所有的写入命令会追加到 aof_buf(缓冲区)中。
AOF 缓冲区根据对应的策略向硬盘做同步操作。
随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
评论