Redis 提供了两种持久化策略git
RDB 持久化机制,会在一段时间内生成指定时间点的数据集快照(snapshot)github
AOF 持久化机制,记录 server 端收到的每一条写命令,当 server 重启时会进行重放以此来重建以前的数据集。AOF 文件中的命令所有以 Redis 协议的格式来保存,新命令会被追加(append)到文件的末尾。 Redis 还能够在后台对 AOF 文件进行重写(rewrite) ,使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。redis
若是你仅使用 Redis 做为缓存加速访问,你能够关闭这两个持久化设置算法
你也能够同时开启这两个持久化设置,可是在这种状况下,Redis 重启时会使用 AOF 文件来重建数据集,由于 AOF 文件保存的数据每每更加完整数据库
Redis 提供了 SAVE 和 BGSAVE 两个命令来生成 RDB 文件,区别是前者是阻塞的,后者是后台 fork 子进程进行不会阻塞主进程处理命令请求。载入 RDB 文件不须要手工运行,而是 server 端自动进行,只要启动时检测到 RDB 文件存在 server 端便会载入 RDB 文件重建数据集。固然上面简介中已经提到,若是 同时存在 AOF 的话会优先使用 AOF 重建数据集由于其保存的数据更完整。缓存
SAVE 和 BGSAVE 命令实现安全
C服务器
void saveCommand(client *c) {
// BGSAVE执行时不能执行SAVE
if (server.rdb_child_pid != -1) {
addReplyError(c,"Background save already in progress");
return;
}
rdbSaveInfo rsi, *rsiptr;
rsiptr = rdbPopulateSaveInfo(&rsi);
// 调用rdbSave函数执行备份(阻塞当前客户端)
if (rdbSave(server.rdb_filename,rsiptr) == C_OK) {
addReply(c,shared.ok);
} else {
addReply(c,shared.err);
}
}
/*
* BGSAVE 命令实现 [可选参数"schedule"]
*/
void bgsaveCommand(client *c) {
int schedule = 0;
/* 当AOF正在执行时,SCHEDULE参数修改BGSAVE的效果
* BGSAVE会在以后执行,而不是报错
* 能够理解为:BGSAVE被提上日程
*/
if (c->argc > 1) {
// 参数只能是"schedule"
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"schedule")) {
schedule = 1;
} else {
addReply(c,shared.syntaxerr);
return;
}
}
// BGSAVE正在执行,不操做
if (server.rdb_child_pid != -1) {
addReplyError(c,"Background save already in progress");
} else if (server.aof_child_pid != -1) {
// aof正在执行,若是schedule==1,BGSAVE被提上日程
if (schedule) {
server.rdb_bgsave_scheduled = 1;
addReplyStatus(c,"Background saving scheduled");
} else {
addReplyError(c,
"An AOF log rewriting in progress: can't BGSAVE right now. "
"Use BGSAVE SCHEDULE in order to schedule a BGSAVE whenever "
"possible.");
}
} else if (rdbSaveBackground(server.rdb_filename,NULL) == C_OK) {// 不然调用rdbSaveBackground执行备份操做
addReplyStatus(c,"Background saving started");
} else {
addReply(c,shared.err);
}
}
SAVE POINT save <seconds> <changes>app
你能够配置保存点(save point),Redis 若是每 N 秒数据发生了 M 次改变就保存快照文件,例以下面:函数
Save Point 配置
Shell
# 这个保存点配置表示每60秒,若是数据发生了1000次以上的变更,Redis就会自动保存快照文件
save 60 1000
# 保存点能够设置多个,Redis的配置文件就默认设置了3个保存点
# 格式为:save <seconds> <changes>
# 能够设置多个。
save 900 1 #900秒后至少1个key有变更
save 300 10 #300秒后至少10个key有变更
save 60 10000 #60秒后至少10000个key有变更
stop-writes-on-bgsave-error yes | no
若是 Redis 执行 RDB 持久化失败(常见于操做系统内存不足),那么 Redis 将再也不接受 client 写入数据的请求。固然在实践中,咱们一般会将 stop-writes-on-bgsave-error 设置为 false,同时让监控系统在 Redis 执行 RDB 持久化失败时发送告警,以便介入解决,而不是粗暴地拒绝 client 的写入请求。
rdbcompression yes | no
当生成 RDB 文件时,Redis 会判断字符串长度 >=20字节则压缩,不然不压缩存储,默认 Redis 会采用 LZF 算法进行数据压缩。
rdbchecksum yes | no
从版本5的 RDB 的开始,一个 CRC64 的校验码会放在文件的末尾。这样更能保证文件的完整性,可是在保存或者加载文件时会损失必定的性能(大概10%)。若是想追求更高的性能,能够把它禁用掉,这样文件在写入校验码时会用 0 替代,加载的时候看到 0 就会直接跳过校验。
RDB文件是一个很简洁的单文件,它保存了某个时间点的 Redis 数据集,很适合用于作备份。你能够设定一个时间点对 RDB 文件进行归档,这样就能在须要的时候很轻易的把数据恢复到不一样的版本。
基于上面所描述的特性,RDB 文件很适合用于灾备,由于单文件能够很方便地传输到另外的数据中心。
RDB的性能很好,须要进行持久化时,主进程会 fork 一个子进程出来,而后把持久化的工做交给子进程,本身不会有相关的 I/O 操做。
比起 AOF,在数据量比较大的状况下,RDB的启动速度更快。
RD B容易形成数据的丢失,当你但愿在 Redis 中止工做时尽可能减小数据丢失的话,那 RDB 不适用。假设每5分钟保存一次快照,若是Redis由于某些缘由不能正常工做,那么从上次产生快照到 Redis 出现问题这段时间的数据就会丢失了。你能够经过配置不一样的 save point 来减轻数据丢失的程度,可是越紧凑的 save point 会越频繁地触发 RDB 生成操做,从而对 Redis 性能产生影响
RDB 使用 fork 子进程进行数据的持久化,若是数据比较大的话可能就会花费点时间,形成 Redis 中止服务几毫秒,若是数据量很大且 CPU 性能不是很好的时候,中止服务的时间甚至会到一秒。AOF 也须要 fork 可是你能够本身调整 rewrite 的频率,它不会形成数据丢失。在 Linux 系统中,fork 会拷贝进程的 page table。随着进程占用的内存越大,进程的 page table 也会越大,那么 fork 也会占用更多的时间。 若是 Redis 占用的内存很大 (例如 20 GB),那么在 fork 子进程时,会出现明显的停顿现象(没法处理 client 的请求)。另外,在不一样机器上,fork 的性能是不一样的,能够参见 Fork time in different systems
Linux fork 子进程采用的是 copy-on-write 的方式。在 Redis 执行 RDB 持久化期间,若是 client 写入数据很频繁,那么将增长 Redis 占用的内存,最坏状况下,内存的占用将达到原先的两倍。
和 RDB 持久化数据库键值对来记录数据库状态不一样,AOF 是经过保存对数据库的写命令集来记录数据库状态的。AOF 持久化实现能够分为命令追加(append)、文件写入(write)、文件同步(fsync) 三个步骤。Append 追加命令到 AOF 缓冲区,Write 将缓冲区的内容写入到程序缓冲区,Fsync 将程序缓冲区的内容写入到文件。
命令追加
当 AOF 持久化功能打开时,server 端每执行完一个写命令,会以协议格式将被执行的写命令追加到 server 端 redisServer 结构体中的 aof_buf 缓冲区末尾。
文件写入与同步
Redis server 进程是一个事件循环(event loop),server 每结束一个事件循环以前都会调用 flushAppendOnlyFile 函数,考虑是否将 aof_buf 缓冲区中的内容吸入和保存到 AOF 文件,而 flushAppendOnlyFile 函数的行为由 appendfsync 选项来控制
appendfsync 值 |
flushAppendOnlyFile 行为 |
---|---|
always |
每一个事件循环都将 aof_buf 缓冲区中的内容写入 AOF 文件,而且调用 fsync() 将其同步到磁盘。这能够保证最好的数据持久性,但却会给系统带来极大的开销,其效率是三者中最慢的,但同时安全性也是最高的,即便宕机也只丢失一个事件循环中的数据。 |
no |
每一个事件循环都将 aof_buf 缓冲区中的内容写入 AOF 文件,但不对其进行同步,什么时候同步至磁盘会让操做系统决定。这种模式下 AOF 的写入速度最快,不过因其会在系统缓存中积累一段时间的数据,因此同步时间为三者最长。一旦宕机将会丢失自上一次同步 AOF 文件起全部的数据。 |
everysec |
每一个事件循环都将 aof_buf 缓冲区中的内容写入 AOF 文件,Redis 还会每秒在子线程中执行一次 fsync()。在实践中,推荐使用这种设置,必定程度上能够保证数据持久性,又不会明显下降 Redis 性能。 |
AOF 持久化并非没有缺点的,Redis 会不断将接收到的写命令追加到 AOF 文件中,致使 AOF 文件愈来愈大。过大的 AOF 文件会消耗磁盘空间,而且致使 Redis 重启时更加缓慢。为了解决这个问题,在适当状况下,Redis 会对 AOF 文件进行重写,去除文件中冗余的命令,以减少 AOF 文件的体积。
AOF的重写会执行大量的写入操做,Redis是单线程的,因此若是有服务器直接调用重写,服务器就不能处理其余命令了,所以Redis服务器新起了单独一个进程来执行AOF重写。固然也能够经过 BGREWRITEAOF 命令手动重写 AOF 文件。重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。
在子进程执行AOF重写时,服务端接收到客户端的命令以后,先执行客户端发来的命令,而后将执行后的写命令追加到AOF缓冲区中,同时将执行后的写命令追加到AOF重写缓冲区中。 等到子进程完成了重写工做后,会发一个完成的信号给服务器,服务器就将AOF重写缓冲区中的全部内容追加到AOF文件中,而后原子性地覆盖现有的AOF文件。
aof 相关配置
Shell
# 你能够在 redis.conf 中经过如下配置开启 AOF 功能
appendonly yes
# 文件存放目录,与RDB共用。默认为当前工做目录。
dir ./
# 默认文件名为appendonly.aof
appendfilename "appendonly.aof"
# fsync 相关配置
# appendfsync always
appendfsync everysec
# appendfsync no
# Redis会记住自从上一次重写后AOF文件的大小(若是自Redis启动后还没重写过,则记住启动时使用的AOF文件的大小)。
# 若是当前的文件大小比起记住的那个大小超过指定的百分比,则会触发重写。
# 同时须要设置一个文件大小最小值,只有大于这个值文件才会重写,以防文件很小,可是已经达到百分比的状况。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 上面两个配置的做用:当 AOF 文件的体积大于 64MB,而且 AOF 文件的体积比上一次重写以后的体积大了至少一倍,那么 Redis 就会执行 AOF 重写。
# 要禁用自动的日志重写功能,咱们能够把百分比设置为0:
auto-aof-rewrite-percentage 0
比RDB可靠。你能够制定不一样的 fsync 策略:no、everysec 和 always。默认是 everysec。这意味着你最多丢失一秒钟的数据。
AOF日志文件是一个纯追加的文件。就算是遇到忽然停电的状况,也不会出现日志的定位或者损坏问题。甚至若是由于某些缘由(例如磁盘满了)命令只写了一半到日志文件里,咱们也能够用 redis-check-aof 这个工具很简单的进行修复。
当AOF文件太大时,Redis 会自动在后台进行重写。重写很安全,由于重写是在一个新的文件上进行,同时 Redis 会继续往旧的文件追加数据。新文件上会写入能重建当前数据集的最小操做命令的集合。当新文件重写完,Redis 会把新旧文件进行切换,而后开始把数据写到新文件上。
AOF 把操做命令以简单易懂的格式一条接一条的保存在文件里,很容易导出来用于恢复数据。例如咱们不当心用 FLUSHALL 命令把全部数据刷掉了,只要文件没有被重写,咱们能够把服务停掉,把最后那条命令删掉,而后重启服务,这样就能把被刷掉的数据恢复回来。
在相同的数据集下,AOF 文件的大小通常会比 RDB 文件大。
在某些 fsync 策略下,AOF 的速度会比 RDB 慢。一般 fsync 设置为每秒一次就能得到比较高的性能,而在禁止 fsync 的状况下速度能够达到 RDB 的水平。
在过去曾经发现一些很罕见的BUG致使使用AOF重建的数据跟原数据不一致的问题。
Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,能够经过配置项 aof-use-rdb-preamble 开启)。若是把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样作的好处是能够结合 RDB 和 AOF 的优势, 快速加载同时避免丢失过多的数据。固然缺点也是有的, AOF 里面的 RDB 部分就是压缩格式再也不是 AOF 格式,可读性较差。