什么是Redis持久化:
持久化:将内存中的数据库状态固化到磁盘的过程。
Redis默认配置包含16个database,dbid介于0和databases-1之间的数字,默认数据库是DB 0,可以使用select <dbid>在每个连接上选择一个不同的数据库,非空数据库以及它们包含的键值对统称为数据库状态。
Redis是内存型数据库,运行时数据均在内存中,如果服务器宕机,那么数据将全部丢失。如何来保证Redis数据的安全性、完整性以及高可用,Redis数据的持久化机制功不可没。
Redis的两个核心流程:文件事件和时间事件
文件事件常见的有:接收连接(accept)、读取(read)、写入(write)、关闭连接(close)等。
时间事件常见的是:serverCron,默认情况下,serverCron每100ms会被触发一次,执行的操作有:清理过期键、RDB的save point检查、AOF后台重写将 aof_buf 内容写到磁盘上等等。
持久化方式:
有3种持久化方式:RDB、AOF及混合持久化。
一、RDB
RDB全称Redis Database Backup file(Redis数据备份文件),也被称为Redis数据快照。Redis会将内存中的数据库状态(数据库键值对等信息)保存到磁盘上,生成的RDB快照文件是一个经过压缩的二进制文件通过该文件,可以还原生成RDB文件时的数据库状态。生成RDB文件的工作在源代码中由src/rdb.c文件中的rdbSave()方法完成。默认条件下,rdb文件会被放置于src/dump.rdb。
命令:SAVE、BGSAVE
Save:前台执行,生成RDB文件的时候,会阻塞整个Redis实例,在RDB生成过程中,无法处理其他请求。对于内存很大的实例,该过程非常耗时,通常不会直接使用该命令。
伪代码:
void SAVE(){ rdbSave(); } |
Bgsave:后台执行,主进程fork出子进程来生成RDB快照文件,阻塞只会发生在fork出的子进程中,主进程可以正常处理其他请求。但是,当Bgsave命令执行期间,服务器会拒绝客户端发送的所有save、bgsave命令,防止产生竞争。
注意:后面会提到AOF持久化方式中的bgrewriteaof命令,当bgsave执行时,客户端发送的bgrewriteaof命令会被服务器暂时阻塞,等bgsave命令执行完毕后会被调起执行;但是,当bgrewriteaof执行时,客户端发送的bgsave命令会被服务器拒绝。 |
子进程会copy父进程的page table。如果实例占用的内存很大,fork子进程会出现明显的卡顿现象。
伪代码:
void BGSAVE(){ // 创建子进程 pid = fork(); If(pid == 0){ // 子进程负责创建RDB文件 rdbSave(); // 向父进程发送信号 signal_parent(); } else if(pid > 0) { // 父进程继续处理其他请求,并通过轮询等待子进程的信号 handle_request_and_wait_signal(); } else { // fork子进程出错 handle_fork_error(); } } |
RDB文件载入
Redis服务器启动的时候,当检测到RDB文件存在的时候,会自动将其载入。在载入RDB文件期间,服务器会一直处于阻塞状态。载入RDB文件的工作在源代码中由src/rdb.c文件中的rdbLoad()方法完成。
注意:由于AOF文件的更新频率相对于RDB文件的更高,如果服务器开启了AOF持久化,那么当服务器启动的时候,会优先使用AOF文件还原数据库状态。只有当AOF持久化功能关闭的时候,服务器才会使用RDB文件来还原数据库状态。流程如下:
RDB文件不存在时候,服务启动的打印数据如下:
RDB文件存在时候,服务启动的打印数据如下:
RDB触发配置:
# 最近900秒(15分钟)内 至少产生1次写入
save 900 1
# 最近300秒(5分钟)内 至少产生10次写入
save 300 10
# 最近60秒(1分钟)内 至少产生10000次写入
save 60 10000
可以在所有配置之后加上如下配置,取消RDB触发配置:
Save “”
服务器在save配置条件被触发时候,输出日志如下:
RDB文件结构
REDIS | db_version | databases | EOF | check_sum |
REDIS:5个字节的常量;
db_version:4个字节,记录RDB文件的版本号;
databases:包含0个或者任意多个数据库,以及各个数据库中的键值对数据;
EOF:1个字节的常量,标志着RDB文件正文结束;
check_sum:8字节唱的无符号整数,前面4部分数据计算出的一个校验和,服务器在载入RDB文件的时候,会将载入数据所计算出的校验和与check_sum所记录的校验和进行对比,防止RDB文件出错或者损坏。
分析RDB文件
命令:od -c dump.rdb
优点:
- RDB文件是经过压缩的二进制文件,占用空间小;
- RDB非常适用于灾难回复,由于结构紧凑,可以将其传输至其他数据中心;
- RDB采用fork子进程的方式处理所有的保存工作,父进程无需执行任何磁盘I/O操作;
- RDB在恢复大数据集时的速度非常快。
缺点:
- RDB保存的是某一时刻的数据快照,容易造成数据丢失。虽然可以通过save point配置持久化频率,但是,RDB是需要保存整个数据集的状态,频率太高容易造成Redis性能问题;
- RDB使用fork子进程进行持久化,如果数据集太大,fork子进程copy父进程的page table会非常耗时,也会造成Redis停止服务一段时间;
- Linux fork子进程采用copy-on-write方式。主进程在修改的同时,需要copy一份出来共子进程写入RDB文件,极端情况下,所有的page都被修改,此时内存占用是原来的2倍。
适用场景:
- 主从全量同步数据;
- 数据备份,Redis主从全量同步数据就是使用的RDB文件进行的;
数据丢失不敏感的场景,可以用来进行实例宕机后恢复数据。
二、AOF
AOF全称Append Only File(追加日志文件),AOF记录的是每一个命令的详细信息,包括完整的命令类型、参数等。只要产生写命令,就会实时写入到AOF文件中。
开启AOF持久化之后,服务启动的打印数据如下:
AOF实现步骤:
命令追加、文件写入、文件同步(刷盘)。
命令追加:当AOF持久化功能打开时,服务器执行完一个命令,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾。
例如,客户端向服务器发送如下命令:
会将以下协议内容追加到aof_buf缓冲区的末尾:
文件写入与同步:
文件写入:将append的命令,写入aof_buf缓冲区;
文件同步:将aof_buf缓冲区中的所有命令,持久化到磁盘。
文件写入与同步的效率和安全性,取决于AOF持久化刷盘方式配置appendfsync,下文详细讲解。
AOF触发配置:
# 开启AOF
appendonly yes
# AOF文件名
appendfilename "appendonly.aof"
# 文件刷盘方式
appendfsync everysec
AOF持久化默认是关闭的,可以通过配置appendonly yes开启。
AOF刷盘方式
1、appendfsync always
每次写入都刷盘,对性能影响最大,占用磁盘IO比较高,数据安全性最高。
2、appendfsync everysec
默认配置,1秒刷一次盘,对性能影响相对较小,节点宕机时最多丢失1秒的数据。
3、appendfsync no
按照操作系统的机制刷盘,对性能影响最小,数据安全性低,节点宕机丢失数据取决于操作系统刷盘机制。
AOF文件在载入与数据还原
- 创建不带网络连接的伪客户端;
- 从AOF文件分析并读取一条写命令;
- 使用伪客户端执行被读取出的写命令;
- 重复执行步骤2和步骤3,知道AOF文件所有命令都被处理完毕位置。
流程图如下:
优点:
- AOF数据文件更新比较及时,可以降低数据丢失的风险,在RDB和AOF同时开启的情况下,Redis会优先使用AOF文件进行数据恢复;
- AOF文件是一个纯追加的日志文件,即使由于某些原因包含了未写入完整的命令,也可以通过redis-check-aof工具修复;
- AOF文件有序的保存了对数据执行的所有写入操作,文件内容易懂。
缺点:
- AOF文件一般会比RDB文件大;
- 默认情况下,AOF的速度会比RDB慢;
- AOF文件刷盘会增加磁盘IO的负担,可能影响Redis的性能(开启每秒刷盘时);
- 随着时间增长,AOF文件会越来越大。
AOF重写
AOF文件太大的时候,Redis会自动在后台进行重写,重写后的AOF文件包含了恢复当前数据集所需的最小命令集合(这个重写过程会消耗大量的CPU资源)。
AOF 重写由两个参数共同控制,auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size,同时满足这两个条件,则触发 AOF 后台重写 BGREWRITEAOF。
// 当前AOF文件比上次重写后的AOF文件大小的增长比例超过100 auto-aof-rewrite-percentage 100 // 当前AOF文件的文件大小大于64MB auto-aof-rewrite-min-size 64mb |
auto-aof-rewrite-percentage 0,指定0的百分比,以禁用自动AOF重写功能。
AOF重写的实现
Redis的AOF文件重写并不是对现有AOF文件进行读取、分析和写入,它是通过读取服务器当前的数据库状态来实现的。
适用场景:
对于丢失数据较敏感的场景。
- 混合持久化
混合持久化并不是一种全新的持久化方式,而是对已有方式的优化。其本质是通过 AOF 后台重写(bgrewriteaof 命令)完成的,不同的是当开启混合持久化时,fork 出的子进程先将当前全量数据以 RDB 方式写入新的 AOF 文件,然后再将 AOF 重写缓冲区(aof_rewrite_buf_blocks)的增量命令以 AOF 方式写入到文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。
优点:
结合 RDB 和 AOF 的优点, 更快的重写和恢复。
缺点:
AOF 文件里面的 RDB 部分不再是 AOF 格式,可读性差。
总结:
RDB | AOF | |
持久化方式 | 生成某一时刻的数据快照文件 | 实时记录每一个写命令到文件 |
数据完整性 | 不完整,取决于备份周期 | 相对完整性高,取决于文件刷盘方式 |
文件大小 | 压缩二进制写入,文件较小 | 原始的操作命令,文件大 |
宕机恢复时间 | 快 | 慢 |
恢复优先级 | 低 | 高 |
持久化代价 | 高,消耗大量CPU和内存 | 低,只占用磁盘IO资源 |
使用场景 | 数据备份、主从全量复制、对丢数据不敏感的业务场景快速数据恢复 | 对于丢失数据敏感的场景,例如涉及金钱交易相关的业务 |