我们知道,Redis 是基于 key-value 的 NoSQL 数据库,它的最大特点是速度非常快,几乎是 MySQL 的十万倍;而支撑 Redis 速度快特性的最主要原因是 Redis 中的数据都存储在内存中。
这里就引出了一个问题 – 内存中的数据是掉电易失的,且一旦进程退出内存中的对应数据也会被释放,那么我们要如何保证 Redis 数据库中的数据安全?
答案很简单,将内存中的数据持久化一份到硬盘中就好了,这样当我们读取数据时直接从内存中读取,当我们写入数据时同时写入内存与硬盘。但将数据写入到硬盘的速度是远慢于写入内存的,这会导致 Redis 的速度变慢,为了就可能降低数据写入硬盘对 Redis 性能带来的影响,Redis 提出不同的持久化策略 – RDB 与 AOF。
RDB 全称是 Redis Database,它的思想是定期将内存中的数据生成快照保存到硬盘中,即每隔一段时间将内存中的 key-value 键值对写入到特定的磁盘文件中,当 Redis 服务重启时,使用该文件中的数据来恢复内存中的数据。
RDB 定期将内存数据快照保存到硬盘中,这里的定期具体表现为两种方式 – 自动触发和手动触发。
手动触发是指程序员通过 Redis 客户端执行特定的命令来触发快照生成,包括:
自动触发是指在配置文件 redis.conf
中进行相关项的配置,让 Redis 服务器达到某种条件时 (间隔多长时间以及发生了多少次修改) 自动触发 bgsave 命令,用户无感知。具体如下:
################################ SNAPSHOTTING ################################
#
# Save the DB on disk:
#
# save <seconds> <changes>
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""
save 900 1
save 300 10
save 60 10000
注意配置项的描述 – 只有当经过的秒数以及最少修改次数都达标时,Redis 才会自动触发 bgsave。
下面我们来介绍一下 bgsave
命令的运行流程:
如上图所示:
task_struct
以及 mm_struct
等内存数据结构,使得子进程能够访问所有父进程的内存数据。
"Background saving started"
信息并不再阻塞父进程,可以继续响应其他命令。
关于 RDB 文件,有一些需要注意的地方:
/var/lib/redis/
,可以通过配置文件中的 dir
选项进行配置; 默认文件名为 dump.rdb
,可以通过配置文件中的 dbfilename
选型进行配置。redis-check-dump
工具,检测 RDB 文件并获取对应的错误报告。shutdown
命令,会自动执行 bgsave 生成 RDB 文件,便于下次重启时恢复内存数据;但如果 Redis 服务器异常退出,比如 kill -9
或者 掉电等,就会来不及生成 RDB 文件,导致部分内存数据丢失。优点:
缺点:
我们上面学习了 RDB 持久化方式,RDB 最大的缺点在于无法做到实时持久化,两次生成快照之间的实时数据可能随着服务器异常退出而丢失。为了解决这个问题,Redis 提出了 AOF (Append Only File) 持久化。
AOF 以独立日志的方式来记录每次写命令,即将用户的每个修改操作都记录到文件中;Redis 重启时再从文件中读取这些命令然后重新执行,达到恢复数据的效果。AOF 解决了数据持久化的实时性,目前已经成为 Redis 持久化的主流方式。
AOF 的使用方式如下:
appendonly
选项为 yes
;appendonly.aof
,可以通过 appendfilename
选项进行配置。/var/lib/redis
,与 RDB 一样,可以通过 dir
选项进行配置。############################## APPEND ONLY MODE ###############################
# By default Redis asynchronously dumps the dataset on disk. This mode is
# good enough in many applications, but an issue with the Redis process or
# a power outage may result into a few minutes of writes lost (depending on
# the configured save points).
#
# The Append Only File is an alternative persistence mode that provides
# much better durability. For instance using the default data fsync policy
# (see later in the config file) Redis can lose just one second of writes in a
# dramatic event like a server power outage, or a single write if something
# wrong with the Redis process itself happens, but the operating system is
# still running correctly.
#
# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
#
# Please check http://redis.io/topics/persistence for more information.
appendonly yes
# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof"
需要注意的是,当开启 AOF 后,RDB 就失效了,即服务器退出时不再自动生成 RDB 文件,服务器重启时也不使用 RDB,而是 使用 AOF 文件进行数据恢复。
既然 AOF 能做到实时持久化,那是不是意味着内存中的数据一定不会丢失?
不是,AOF 下也可能存在数据丢失:
AOF 的工作流程如下:
我们上面提到 AOF 实际上是先将数据写入内存缓冲区中,那么此时缓冲区的刷新频率就直接与数据可靠性挂钩 – 缓冲区刷新频率越高,数据可靠性越高,但相对的服务器性能就越低;缓冲区刷新频率越低,数据可靠性越低,但相对的服务器性能就越高。但无论刷新频率多高,只要不是一有修改就立即写入硬盘,就存在数据丢失的风险。
因此,AOF 可以看作是效率与数据安全的一种折中方案,同时,为了满足不同应用场景下对效率以及数据安全的不同要求,Redis 支持用户对缓冲区刷新策略进行配置。可选的配置方案如下:
-可配置值 | -说明 |
---|---|
always | 命令写入 aof_buf 后调用立即 fsync 同步,完成后返回 |
everysecs | 命令写入 aof_buf 后只执行 write 操作,不进行 fsync,每秒由同步线程进行 fsync。 |
no | 命令写入 aof_buf 后只执行 write 操作,由 OS 控制 fsync。 |
关于 write 与 fsync:
Redis 默认使用 everysecs
作为缓冲区刷新策略,可以通过配置文件的 appendfsync
选项进行配置。
# The fsync() call tells the Operating System to actually write data on disk
# instead of waiting for more data in the output buffer. Some OS will really flush
# data on disk, some other OS will just try to do it ASAP.
#
# Redis supports three different modes:
#
# no: don't fsync, just let the OS flush the data when it wants. Faster.
# always: fsync after every write to the append only log. Slow, Safest.
# everysec: fsync only one time every second. Compromise.
#
# The default is "everysec", as that's usually the right compromise between
# speed and data safety. It's up to you to understand if you can relax this to
# "no" that will let the operating system flush the output buffer when
# it wants, for better performances (but if you can live with the idea of
# some data loss consider the default persistence mode that's snapshotting),
# or on the contrary, use "always" that's very slow but a bit safer than
# everysec.
#
# More details please check the following article:
# http://antirez.com/post/redis-persistence-demystified.html
#
# If unsure, use "everysec".
# appendfsync always
appendfsync everysec
# appendfsync no
由于 AOF 是不断向文件末尾追加内容的,因此随着命令不断写入 AOF,文件会逐渐变大,为了解决这个问题,Redis 引入 AOF 重写机制来压缩文件体积。较小的 AOF 文件一方面降低了硬盘空间占用,另一方面可以提升启动 Redis 时数据恢复的速度。
重写后的 AOF 为什么可以变小?有如下原因:
因此,AOF 文件重写其实是把 Redis 进程内的数据转化为写命令同步到新的 AOF 文件。
因此,我们也可以认为重写是保存当前内存数据的快照。
Redis 中,混合持久化默认开启,我们可以通过配置文件中的 aof-use-rdb-preamble
配置项进行关闭。
# When rewriting the AOF file, Redis is able to use an RDB preamble in the
# AOF file for faster rewrites and recoveries. When this option is turned
# on the rewritten AOF file is composed of two different stanzas:
#
# [RDB file][AOF tail]
#
# When loading Redis recognizes that the AOF file starts with the "REDIS"
# string and loads the prefixed RDB file, and continues loading the AOF
# tail.
aof-use-rdb-preamble yes
AOF 重写过程可以手动触发和自动触发:
bgrewriteaof
命令。auto-aof-rewrite-min-size
和 auto-aof-rewrite-percentage
配置项确定自动触发时机。 auto-aof-rewrite-min-size
:表示触发重写时 AOF 的最小文件大小,默认为 64MB。auto-aof-rewrite-percentage
:代表当前 AOF 占用大小相比较上次重写时增加的比例。# Automatic rewrite of the append only file.
# Redis is able to automatically rewrite the log file implicitly calling
# BGREWRITEAOF when the AOF log size grows by the specified percentage.
#
# This is how it works: Redis remembers the size of the AOF file after the
# latest rewrite (if no rewrite has happened since the restart, the size of
# the AOF at startup is used).
#
# This base size is compared to the current size. If the current size is
# bigger than the specified percentage, the rewrite is triggered. Also
# you need to specify a minimal size for the AOF file to be rewritten, this
# is useful to avoid rewriting the AOF file even if the percentage increase
# is reached but it is still pretty small.
#
# Specify a percentage of zero in order to disable the automatic AOF
# rewrite feature.
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
AOF 的重写流程如下图所示:
上面有两个比较重要的细节:
aof_rewrite_buf
,用来将这段时间内的修改操作追加到新 AOF 文件中。AOF 持久化的优点在于内存数据更加安全,不易丢失。缺点也很明显,一是频繁的写入硬盘导致效率降低;二是以文本格式存储 AOF 修改操作,服务器重启时恢复数据需要重新执行 AOF 文件中的命令,不过混合持久化对这一点进行了优化。
redis-server 启动时,会根据 RDB 和 AOF 文件的内容进行数据恢复。恢复过程如下:
可以看到,AOF 是要优先于 RDB 的,只有当 AOF 未开启时,redis-server 才会使用 RDB 文件进行数据恢复。