Redis 的读写操作都是在内存中,所以 Redis 性能才会高,但是当 Redis 重启后,内存中的数据就会丢失,为了保证内存中的数据不会丢失,Redis 实现了数据持久化的机制,这个机制会把数据存储到磁盘,这样在 Redis 重启就能够从磁盘中恢复原有的数据。
redis提供了两种持久化的方式,即 RDB(Redis Database)快照和 AOF(Append-Only File)日志。
一、AOF
AOF 持久化会以日志追加的方式记录每个写操作,将操作以文本格式追加到 AOF 文件的末尾。当 Redis 重新启动时,会通过重新执行 AOF 文件中的命令来重建数据集。因为 AOF 文件包含了写操作的完整日志,所以可以确保数据的完整性。
在 Redis 中 AOF 持久化功能默认是不开启的,需要修改配置文件 redis.conf
#是否启用aof,默认no,不启用
appendonly yes
#aof持久化文件的名称
appendfilename "appendonly.aof"
注:redis 是在执行写操作命令后,再将该命令记录到 AOF日志中的。
1.1 写回策略
Redis 写入 AOF 日志的过程,如下图:
详细过程:
- redis 执行完写操作命令后,将命令追加到
server.aof_buf
缓冲区; - 然后通过 write() 系统调用,将 aof_buf 缓冲区的数据写入到 AOF 文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区 page cache,等待内核将数据写入硬盘;
- 具体内核缓冲区的数据什么时候写入到硬盘,由内核决定。
内核提供了三种写回策略控制内核缓冲区的数据什么时候写入到硬盘
- always:每次有写操作发生时,都会将命令追加到 AOF 缓冲区,并立即将缓冲区刷写到磁盘
- everysec:每秒将 AOF 缓冲区的命令刷写到磁盘一次
- no:将 AOF 缓冲区的命令交给操作系统来处理,由操作系统负责将数据刷写到磁盘
在配置文件中,由 appendfsync
三个策略各有其优缺点
写回策略 |
写回时间 |
优点 |
缺点 |
always |
同步写回 |
可靠性高,最大程度保证数据不丢失 |
每个写操作都要写回磁,性能开销大 |
everysec |
每秒写回 |
性能适中 |
宕机时会丢失1秒的数据 |
no |
由操作系统决定 |
性能好 |
宕机时会丢失较多数据 |
1.2 重写机制
AOF 日志是一个文件,随着执行的写操作命令越来越多,文件的大小会越来越大。
如果当 AOF 日志文件过大就会带来性能问题,比如重启 Redis 后,需要读 AOF 文件的内容以恢复数据,如果文件过大,整个恢复的过程就会很慢。
所以,Redis 为了避免 AOF 文件越写越大,提供了 AOF 重写机制,当 AOF 文件的大小超过所设定的阈值后,Redis 就会启用 AOF 重写机制,来压缩 AOF 文件。
AOF 重写机制是在重写时,读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到「新的 AOF 文件」,等到全部记录完后,就将新的 AOF 文件替换掉现有的 AOF 文件。
举个例子,在没有使用重写机制前,假设前后执行了「set name xiaolin」和「set name xiaolincoding」这两个命令的话,就会将这两个命令记录到 AOF 文件。
但是在使用重写机制后,就会读取 name 最新的 value(键值对) ,然后用一条 「set name xiaolincoding」命令记录到新的 AOF 文件,之前的第一个命令就没有必要记录了,因为它属于「历史」命令,没有作用了。这样一来,一个键值对在重写日志中只用一条命令就行了。
重写工作完成后,就会将新的 AOF 文件覆盖现有的 AOF 文件,这就相当于压缩了 AOF 文件,使得 AOF 文件体积变小了。
然后,在通过 AOF 日志恢复数据时,只用执行这条命令,就可以直接完成这个键值对的写入了。
配置文件相关参数,两参数相互独立
#当 AOF 文件的大小超过上一次重写时的大小的一定比例时,自动触发 AOF 重写。默认值为 100,表示当 AOF 文件大小达到上一次重写大小的两倍时触发重写
auto-aof-rewrite-percentage 100
#当 AOF 文件的大小超过一定阈值时,自动触发 AOF 重写。默认值为 64MB
auto-aof-rewrite-min-size 64mb
注:redis 的重写 AOF 过程是由后台子进程 bgrewriteaof 来完成的;当 redis 在 AOF 重写过程中接收到新的写操作时,它会将这些写操作同时应用到正在进行的 AOF 重写文件和内存中的数据集中。这样可以确保正在进行的 AOF 重写文件和最新的写操作保持同步,避免数据丢失。
二、RDB
RDB 是 Redis 的一种快照持久化方式,它可以将 Redis 在某个时间点上的数据状态保存到磁盘上的二进制文件中。RDB 持久化通过将内存中的数据转储到磁盘上的文件来实现持久化。这个文件是 Redis 数据的紧凑表示,可以在需要时重新加载到内存中,恢复数据。
RDB 持久化的特点包括:
- 紧凑的数据表示:RDB 文件是 Redis 数据在某个时间点上的快照,以二进制格式保存。它是一种紧凑的、压缩的数据表示形式,适合用于备份、迁移和长期存储。
- 高性能:由于 RDB 是通过将数据直接转储到磁盘上的文件,因此在进行快照持久化时,可以达到很高的性能。这是因为写操作不需要同时写入磁盘,减少了磁盘 I/O 操作。
- 容灾恢复:RDB 文件可以用于快速的容灾恢复。在 Redis 重启时,可以加载最新的 RDB 文件,并在较短的时间内将数据还原到内存中,从而快速恢复数据。
- 配置灵活性:可以通过配置文件来控制 RDB 持久化的策略,包括触发快照的方式、时间间隔和条件等。可以根据需求进行定制,灵活地控制持久化的行为。
Redis 提供了两个命令来生成 RDB 文件,分别是 save
和 bgsave
,他们的区别就在于是否在「主线程」里执行:
- 执行了 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程;
- 执行了 bgsave 命令,会创建一个子进程来生成 RDB 文件,这样可以避免主线程的阻塞;
RDB 文件的加载工作是在服务器启动时自动执行的,Redis 并没有提供专门用于加载 RDB 文件的命令。
Redis 还可以通过配置文件的选项来实现每隔一段时间自动执行一次 bgsave 命令,默认会提供以下配置(save需要手动开启,其余默认开启):
#900 秒(15 分钟)内,如果至少有一个键被修改,则触发 RDB 持久化;实际执行的是bgsave命令,会创建子进程来生成 RDB 快照文件
save 900 1
#300 秒(5 分钟)内,如果至少有 10 个键被修改,则触发 RDB 持久化
save 300 10
#60 秒内,如果至少有 10000 个键被修改,则触发 RDB 持久化
save 60 10000
#RDB 文件的保存路径
dir ./
#RDB 文件的名称
dbfilename dump.rdb
#否对 RDB 文件进行压缩,以减少磁盘空间占用
rdbcompression yes
注:Redis 的快照是全量快照,即每次执行快照,都是把内存中的「所有数据」都记录到磁盘中。
执行快照时,数据能被修改吗?
当 RDB 快照正在进行时,Redis 会继续接收和处理客户端的写操作。这意味着在快照过程中,数据可以被修改。修改的数据会被写入内存中,但不会影响当前正在进行的 RDB 快照。
但是,RDB 快照的一致性是在快照开始时确定的,即在执行快照期间修改的数据,不会被复制到当前的快照文件中,只能等待下一次执行快照,如果在 RDB 快照期间发生了大量的写操作,可能会导致 RDB 文件的数据不是最新的。
三、混合使用aof与rdb
从redis 4.0版本开始,redis支持使用混合持久化的方式,即混合使用 AOF 日志和内存快照。
#是否开启混合持久化,默认开启
aof-use-rdb-preamble yes
当开启了混合持久化时,在 AOF 重写日志时,fork
出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。
也就是说,使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据。
这样的好处在于,重启 Redis 加载数据的时候,由于前半部分是 RDB 内容,这样加载的时候速度会很快。
加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少的丢失。