前言
Redis 持久化方式
RDB 持久化
RDB 是默认的持久化方式,类似于 Mysql Dump,这种方式是根据配置的规则定时将内存中数据以快照的方式写入到二进制文件(全量生成一份副本存储到硬盘上,若配置压缩,Redis 会使用 LZF 算法进行压缩,提升主从复制效率),默认的文件名 dump.rdb,可以通过配置设置自动做快照持久化的方式。
RDB 触发机制
save 命令
save 命令是在 Redis 进程中执行的,由于 Redis 是单线程实现,当 save 命令在执行时会阻塞 Redis 服务器一直到该命令执行完成为止,而且该命令会替换旧的 RDB 文件。当数据库数据较多时,应该避免使用该命令。
save 命令的底层函数是 rdbSave(函数内部通过调用 rdbSaveRio 函数来完成真正的转存操作),其将 Redis 数据库 db 保存到磁盘中,如果操作成功函数返回 REDIS_OK,如果操作失败函数返回 REDIS_ERR。
save 命令复杂度:O(N)。
bgsave 命令
bgsave 命令会先使用 Redis 的 fork 函数复制一份当前进程(父进程)的副本(子进程),父进程继续处理来自客户端的请求,子进程开始将内存中的数据写入硬盘中的临时文件,当子进程写完所有的数据后,用该临时文件替换旧的 RDB 文件。由于在子进程中执行 IO 操作,所以 bgsave命令不会阻塞 Redis 服务器进程,Redis 服务器进程在此期间可以继续对外提供服务,但当使用 fork 函数时依然会阻塞 Redis 进程,虽然大部分时候使用 fork 函数是极快的,但极端下仍然出现阻塞现象。
bgsave 命令的底层函数是 rdbSaveBackground,为了提高性能,Redis 服务器在 bgsave 命令执行期间会拒绝执行新到来的其它 bgsave 命令。
如果想知道快照操作是否已经完成,可以使用
lastsave 命令返回最近一次成功执行快照的时间,返回结果是一个 Unix 时间戳。
bgsave 命令复杂度:O(N)。
注意:在执行 fork 时操作系统(类 Unix 操作系统)会使用写时复制
(copy-on-write)策略,即 fork 函数发生的一刻,父进程和子进程共享同一块内存数据,当父进程需要修改其中的某片数据(如执行写命令)时,操作系统会将该片数据复制一份以保证子进程不受影响,所以 RDB 文件存储的是执行 fork 操作那一刻的内存数据。所以 RDB 方式理论上是会存在丢数据的情况的(fork 之后修改的的那些没有写进 RDB 文件)。
** **
save 和 bgsave 命令比较
RDB 配置
** **
RDB 触发场景
** **
RDB 优点
RDB 缺点
RDB 优点
AOF 持久化
AOF 是 Append Only File 的缩写,比 RDB 存储方式有更好的持久化性,它将 Redis 每一个收到的写命令都通过 write 函数追加到文件中(默认是 appendonly.aof),类似于 log(记日志)的过程。当 Redis 重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
AOF 触发机制
Redis 提供了 bgrewriteaof 命令来重写 AOF 文件。
AOF 配置
AOF 文件格式
AOF 文件中的内容完全是以纯文本格式的形式存放。
AOF 执行策略
AOF 持久化功能的实现可以分为 命令传播,缓存追加,文件写入和保存 三个步骤。
命令传播:当一个 Redis 客户端需要执行命令时,它通过网络连接,将协议文本发送给 Redis 服务器。
缓存追加:当 AOF 持久化功能处于打开状态时,服务器接受命令、命令的参数、以及参数的个数、所使用的数据库等信息,将命令还原成 Redis 网络通讯协议,将协议文本追加到 AOF_BUF 末尾。
AOF 文件的写入和保存:Redis 服务器进程是一个事件循环,这个循环中的文件事件负责处理客户端的命令请求,以及向客户端发送命令回复,而时间事件则负责执行像 serverCron 函数这样需要定时运行的函数。因为服务器在处理文件事件时可能会执行写命令,使得一些内容被追加到 AOF_BUF 缓冲区的里面,所以在服务器每次结束一个事件循环之前,都会调用 flushAppendOnlyFile 函数,考虑是否需要将 AOF_BUF 缓冲区中的内容写入和保存到 AOF 文件里面。flushAppendOnlyFile 函数的行为由服务器配置的 appendfsync 的值来决定,主要包括三个配置(默认配置为 AOF_FSYNC_EVERYSEC):
AOF_FSYNC_ALWAYS:命令写入 AOF_BUF 后调用系统 fsync操作同步到 AOF 文件,fsync 完成后线程返回。
AOF_FSYNC_EVERYSEC:命令写入 AOF_BUF 后调用系统 write 操作,write 完成后线程返回。fsync 同步文件操作由专门线程每秒调用一次。
AOF_FSYNC_NO:命令写入 AOF_BUF 后调用系统 write 操作,不对 AOF 文件做 fsync 保存,保存硬盘操作由操作系统负责,通常同步周期最长 30 s。
写入和保存的区别
write,会触发延迟写(delayed write)机制,Linux 在内核提供页缓冲区用来提高硬盘 IO 性能,write 操作在写入系统缓冲区后直接返回,同步硬盘操作依赖于系统调度机制,例如:缓冲区页空间写满或达到特定时间周期。同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
save,根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。fsync 针对单个文件操作(比如 AOF 文件),做强制硬盘同步,fsync 将阻塞直到写入硬盘完成后返回,保证了数据持久化。
** **
AOF 执行策略对性能和安全的影响
对于三种 AOF 保存模式,对 Redis 服务器主进程的阻塞情况如下
AOF_FSYNC_NO,写入和保存都由主进程执行,两个操作都会阻塞主进程。保存操作只会在 AOF 关闭或 Redis 关闭时执行,或者由操作系统触发,在一般情况下,这种策略只需要为写入阻塞,因此它的写入性能很高,当然,这种性能的提高是以降低安全性为代价的:在这种策略下,如果运行的中途发生停机,那么丢失数据的数量由操作系统的缓存冲洗策略决定。
AOF_FSYNC_EVERYSEC,写入操作由主进程执行,阻塞主进程。保存操作由子线程执行,不直接阻塞主进程,但保存操作完成的快慢会影响写入操作的阻塞时长。在通常情况下,最多丢失不多于 2 秒的数据,是一种兼顾性能和安全性的保存方案。
AOF_FSYNC_ALWAYS,写入和保存都由主进程执行,两个操作都会阻塞主进程。这种策略的安全性是最高的,但性能也是最差的,因为服务器必须阻塞直到命令信息被写入并保存到磁盘之后,才能继续处理请求。
因为阻塞操作会让 Redis 主进程无法持续处理请求,所以一般说来,阻塞操作执行得越少、完成得越快,Redis 的性能就越好。
AOF 文件的载入和还原
AOF 重写
AOF 文件通过同步 Redis 服务器所执行的命令,从而实现了数据库状态的记录,但是,随着运行时间的流逝,AOF 文件会变得越来越大,而且,有些被频繁操作的键,对它们所调用的命令可能有成百上千、甚至上万条,AOF 文件的体积就会急速膨胀,对 Redis、甚至整个系统的造成影响。
Redis 重写(rewrite)就是把过期的、没用的、重复的以及可优化的命令,进行化简,从而减少磁盘占用量,加快数据恢复速度。重写创建一个新的 AOF 文件来代替原有的 AOF 文件,新 AOF 文件和原有 AOF 文件保存的数据库状态完全一样,但新 AOF 文件不会包含任何浪费空间的冗余命令,所以新 AOF 文件的体积通常比旧 AOF 文件的体积要小得多。
** **
AOF 重写流程
Redis fork 一个子进程,子进程基于当前内存中的数据,构建日志,开始往一个新的临时的 AOF 文件中写入日志,Redis 主进程,接收到客户端新的写操作之后,在内存中写入日志,同时新的日志也继续写入旧的 AOF 文件,子进程写完新的日志文件之后,Redis 主进程将内存中的新日志再次追加到新的 AOF 文件中,用新的日志文件替换掉旧的日志文件。
AOF 重写方式
bgrewriteaof 命令
Redis 客户端发送 bgrewriteaof 命令,Redis 服务端 fork 一个子进程去完成 AOF 重写,将 Redis 内存中的数据进行一次回溯,回溯成 AOF 文件,并非重写 AOF 文件生成新的 AOF 文件去替换。
AOF 重写配置
auto-aof-rewrite-min-size,AOF文件重写需要的尺寸
auto-aof-rewrite-percentage,AOF文件增长率
aof_current_size 和 aof_base_size,分别用来统计
AOF 当前尺寸(单位:字节)和 AOF 上次启动和重写的尺寸(单位:字节)。
AOF 自动重写的触发时机(同时满足)
** **
AOF 优点
AOF 缺点
AOF 优化
RDB 和 AOF 比较
如果 RDB 在执行 snapshotting 操作,那么 Redis 不会执行 AOF rewrite。如果 Redis 再执行 AOF rewrite,那么就不会执行 RDB snapshotting。
如果 RDB 在执行 snapshotting,此时用户执行
bgrewriteaof 命令,那么等 RDB 快照生成之后,才会去执行 AOF rewrite。
同时有 RDB snapshot 文件和 AOF 日志文件,那么 Redis 重启的时候,会优先使用 AOF 进行数据恢复,因为其中的日志更完整。
开发运维常见问题
优化 fork 操作
fork 是一个同步操作,执行 bgsave 和 bgrewriteaof 时都会执行 fork 操作。
子进程开销和优化
bgsave 和 bgrewriteaof 会进行 fork 操作产生子进程。
CPU
开销:RDB 和 AOF 文件生成属于 CPU 密集型。
优化:不做 CPU 绑定,不和 CPU 密集型应用部署在一起。
内存
开销:fork 内存开销。
优化:echo never /sys/kernel/mm/transparent_hugepage/enabled。
硬盘
开销:AOF 和 RDB 文件写入,可以结合 iostat 和 iotao 分析。
优化:
-
-
- 不要和高硬盘负载服务部署在一起:存储服务、消息队列;
- no-appendfsync-on-rewrite=yes;
- 根据写入量决定磁盘类型:例如 sdd;
- 单机多实例持久化文件目录可以考虑分盘;
AOF 追加阻塞
阻塞定位
Redis日志
info persistence 可以查看上述日志发生的次数。
优化:同子进程的硬盘优化。
参考:
http://doc.redisfans.com/topic/persistence.html
慕课网:一站式学习Redis 从入门到高可用分布式实践
原文始发于微信公众号(一个八月想偷懒的开发坑):
-