目录
RDB
RDB 持久化是把当前进程数据生成快照保存到硬盘的过程。 触发 RDB 持久化过程分为手动触发和自动触发。RDB 完成后会自动生成一个文件,保存在 dir 配置的指定目录下,文件名是 dbfileName 指定。Redis 默认会采用 LZF 算法对生成的 RDB 文件做压缩处理,压缩后的文件远远小于内存大小,默认开启。
手动触发:命令有 save 和 bgsave
- save:该命令会阻塞 Redis 服务器,直到 RDB 的过程完成,已经被废弃,因此线上不建议使用。
- bgsave:每次进行 RDB 进程都会 fork 一个子进程,由子进程完成 RDB 的操作,因此阻塞只会发生在 fork 阶段,一般时间很短。
自动触发:
- 根据我们的 save m n 配置规则自动触发。
- 如果从节点执行全量复制操作, 主节点自动执行 bgsave 生成 RDB 文件并发送给从节点。
- 执行 debug reload 命令重新加载 Redis 时, 也会自动触发 save 操作。
- 默认情况下执行 shutdown 命令时, 如果没有开启 AOF 持久化功能则自动执行 bgsave。
RDB 执行流程:
- 执行 bgsave 命令后,会先判断是否存在 AOF 或者 RDB 的子进程,如果存在,直接返回。
- 父进程 fork 操作创建一个子进程,fork 操作中父进程会被阻塞。
- fork 完成后,子进程开始根据父进程的内存生成临时快照文件,完成后对原有的 RDB 文件进行替换。执行 lastsave 命令可以查看最近一次的 RDB 时间。
- 子进程完成后发送信号给父进程,父进程更新统计信息。
RDB 的优点:
- RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据快照。非常适用于备份,全量复制等场景。比如每 6 小时执行 bgsave 备份,并把 RDB 文件拷贝到远程机器或者文件系统中,用于灾难恢复。
- Redis 加载 RDB 恢复数据远远快于 AOF 的方式。
RDB 的缺点:
- RDB 方式数据没办法做到实时持久化/秒级持久化。因为 bgsave 每次运行都要执行 fork 操作创建子进程,属于重量级操作,频繁执行成本过高。
- RDB 文件使用特定二进制格式保存, Redis 版本演进过程中有多个格式的 RDB 版本, 存在老版本 Redis 服务无法兼容新版 RDB 格式的问题。
AOF
AOF(append only file)持久化,以独立日志的方式记录每次写命令,重启时再重新执行 AOF 文件中的命令达到恢复数据的目的。AOF 的主要作用是解决了数据持久化的实时性, 目前已经是 Redis 持久化的主流方式。
开启 AOF 功能需要设置配置 appendonly yes
, 默认不开启。AOF 文件名通过 appendfilename 配置设置, 默认文件名是 appendonly.aof。保存路径同 RDB 持久化方式一致,通过 dir 配置指定。
AOF 整体的执行流程:大致分为命令写入、文件同步、文件重写、重启加载四个步骤
命令写入
AOF 命令写入的内容直接是文本协议格式。例如 set hello world
这条命 令, 在 AOF 缓冲区会追加如下文本:
*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
命令写入是直接写入到 AOF 的缓冲区中,至于为什么?原因很简单,Redis 使用单线程响应命令,如果每次写 AOF 文件命令都直接追加到硬盘, 那么性能完全取决于当前硬盘负载。先写入缓冲区 aof_buf 中, 还有另一个好处, Redis 可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。
文件同步
Redis 提供了多种 AOF 缓冲区同步文件策略, 由参数 appendfsync 控制,如下:
- 配置为 always 时, 每次写入都要同步 AOF 文件, 在一般的 SATA 硬盘上,Redis 只能支持大约几百 TPS 写入, 显然跟 Redis 高性能特性背道而驰,不建议配置。
- 配置为 no,由于操作系统每次同步 AOF 文件的周期不可控,而且会加大每次同步硬盘的数据量,虽然提升了性能,但数据安全性无法保证。
- 配置为 everysec(默认),是建议的同步策略, 也是默认配置,做到兼顾性能和数据安全性。理论上只有在系统突然宕机的情况下丢失 1 秒的数据(当然,这是不太准确的)。
文件重写
为什么要文件重写呢? 因为文件重写能够使得 AOF 文件的体积变得更小,从而使得可以更快的被 Redis 加载。
随着命令不断写入 AOF, 文件会越来越大, 为了解决这个问题, Redis 引入 AOF 重写机制压缩文件体积。AOF 文件重写是把 Redis 进程内的数据转化为写命令同步到新 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) 的比值。
自动触发时机相当于:
aof_current_size > auto-aof-rewrite-minsize && (aof_current_size - aof_base_size) / aof_base_size >= auto-aof-rewritepercentage
其中,aof_current_size 和 aof_base_size 可以在 info Persistence 统计信息中查看到。
那么文件重写后的 AOF 文件为什么会变小呢? 有如下几个原因:
- 进程内已经超时的数据将不会再次写入 AOF 文件中。
- 旧的 AOF 文件含有无效命令,如 del key1、 hdel key2 等。重写使用进程内数据直接生成,这样新的 AOF 文件只保留最终数据的写入命令。
- 多条写命令可以合并为一个,如:lpush list a、 lpush list b、lpush listc 可以转化为:lpush list a b c。为了防止单条命令过大造成客户端缓冲区溢出,对于 list、set、hash、zset 等类型操作,以 64 个元素为界拆分为多条。
介绍了文件重写的系列知识,下面来看看 Redis 内部是如何进行文件重写的,如下图:
- 重写期间,主线程并没有阻塞,而是在执行其他的操作命令,依然会向旧的 AOF 文件写入数据,这样能够保证备份的最终完整性,如果数据重写失败,也能保证数据不会丢失。
- 为了把重写期间响应的写入信息也写入到新的文件中,因此也会为子进程保留一个缓冲区,防止新写的文件丢失数据。
- 重写是直接把当前内存的数据生成对应命令,并不需要读取老的 AOF 文件进行分析、命令合并。
- AOF 文件直接采用的文本协议,主要是兼容性好、追加方便、可读性高可认为修改修复。
- 无论是 RDB 还是 AOF 都是先写入一个临时文件,然后通过重命名完成文件的替换。
AOF 的优点:使用 AOF 持久化会让 Redis 变得非常耐久:你可以设置不同的 fsync 策略,比如无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据(fsync 会在后台线程执行,所以主线程可以继续努力地处理命令请求)。
AOF 的缺点:
- 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB。在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间。
- 数据恢复速度相对于 RDB 比较慢。
重启加载
无论是 RDB 还是 AOF 都可用于服务器重启时的数据恢复,执行流程如下图:
上图很清晰的分析了 Redis 启动恢复数据的流程,先检查 AOF 文件是否开启,文件是否存在,再检查 RDB 是否开启,文件是否存在。
性能问题与解决方案
通过上面的分析,我们都知道 RDB 的快照、AOF 的重写都需要 fork,这是一个重量级操作,会对 Redis 造成阻塞。因此为了不影响 Redis 主进程响应,我们需要尽可能降低阻塞。
- 优先使用物理机或者高效支持 fork 操作的虚拟化技术。
- 控制 Redis 实例最大可用内存,fork 耗时跟内存量成正比,线上建议每个 Redis 实例内存控制在 10GB 以内。
- 合理配置 Linux 内存分配策略,避免物理内存不足导致 fork 失败。
- 降低 fork 操作的频率,如适度放宽 AOF 自动触发时机,避免不必要的全量复制等。
来源:oschina
链接:https://my.oschina.net/u/4256357/blog/4263369