深入理解redis持久化
持久化方式:
快照(RDB)方式,默认方式,文件以二进制方式保存到RDB文件。
文件追加(AOF)方式,文件以协议文本的方式write到AOF文件。
作用,重启后的数据恢复。当两种方式都启用时,redis会优先载入AOF文件,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
注意优先是指载入AOF文件就不再加载RDB了,不管AOF是不是空文件。
RDB配置:
1 ################################ SNAPSHOTTING #################################
2 # Save the DB on disk:
3 # 设置sedis进行数据库镜像的频率。
4 # 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)。
5 # 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)。
6 # 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)。
7 save 900 1
8 save 300 10
9 save 60 10000
10
11 stop-writes-on-bgsave-error yes
12 # 在进行镜像备份时,是否进行压缩。yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间。
13 rdbcompression yes
14 # 一个CRC64的校验就被放在了文件末尾,当存储或者加载rbd文件的时候会有一个10%左右的性能下降,为了达到性能的最大化,你可以关掉这个配置项。
15 rdbchecksum yes
16 # 快照的文件名
17 dbfilename dump.rdb
18 # 存放快照的目录
19 dir ./
RDB原理:
fork一个子进程,在子进程中循环所有的数据,将数据写到一个临时文件,通过原子性的rename系统调用重命名为dump.rdb。
写操作在一个新的进程中完成,所以rdb文件不会损坏。
在子进程写操作执行的过程中父进程接受到新命令不会同步给子进程。需要等到下一次的写入才能把新命令写入到RDB文件中。
RDB文件对主从同步的作用:
第一次Slave向Master同步的实现是:
Slave向Master发出同步请求,Master先dump出rdb文件,然后将rdb文件全量传输给slave,然后Master把缓存的命令转发给Slave,初次同步完成。
第二次以及以后的同步实现是:
Master将变量的快照直接实时依次发送给各个Slave。但不管什么原因导致Slave和Master断开重连都会重复以上两个步骤的过程。
Redis的主从复制是建立在内存快照的持久化基础上的,只要有Slave就一定会有内存快照发生。
RDB持久化之Redis关闭时的数据保存(总结自参考的第三篇文章,可以看原文,结合代码的):
正常关闭redis,在关闭前会执行save命令,所以在关闭前还未达到持久化条件时的数据也会被持久化了。对于kill也属于正常关闭,只有kill -9 属于异常关闭。
RDB持久化在满足save条件,手动save,正常关闭时数据都会被持久化,只有异常关闭才会造成数据丢失。
注意当RDB持久化关闭后,手动save依然可以持久化数据,只是不能触发自动save,并且正常关闭也不触发save,所以不能说是禁止snapshot,是禁止自动snapshot。
AOF配置:
1 ############################## APPEND ONLY MODE ############################### 2 # 是否开启AOF,默认关闭(no) 3 appendonly yes 4 # 指定 AOF 文件名 5 appendfilename appendonly.aof 6 # Redis支持三种不同的刷写模式: 7 # appendfsync always #每次收到写命令就立即强制写入磁盘,是最有保证的完全的持久化,但速度也是最慢的,一般不推荐使用。 8 appendfsync everysec #每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,是受推荐的方式。 9 # appendfsync no #完全依赖OS的写入,一般为30秒左右一次,性能最好但是持久化最没有保证,不被推荐。 10 11 #在日志重写时,不进行命令追加操作,而只是将其放在缓冲区里,避免与命令的追加造成DISK IO上的冲突。 12 #设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes 13 no-appendfsync-on-rewrite yes 14 #当前AOF文件大小是上次日志重写得到AOF文件大小的二倍时,自动启动新的日志重写过程。 15 auto-aof-rewrite-percentage 100 16 #当前AOF文件启动新的日志重写过程的最小值,避免刚刚启动Reids时由于文件尺寸较小导致频繁的重写。 17 auto-aof-rewrite-min-size 64mb
在使用AOF持久化方式时,Redis会将每一个收到的写命令都通过Write函数追加到文件中
Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写,这些写入操作以 Redis 协议的格式保存。
如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。
压缩aof的持久化文件:bgrewriteaof
收到此命令后Redis将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,
最后替换原来的文件,以此来实现控制AOF文件的增长。
由于是模拟快照的过程,因此在重写AOF文件时并没有读取旧的AOF文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的AOF文件。
总结:aof方式是一直追加文件,设置的fsync的时间是追加的时间,期间新命令一直在内存缓冲区中,触发了fsync条件才执行追加。而bgrewriteaof命令是重写aof文件,只有调用了bgrewriteaof的时候才会产生fork。
AOF重写原理:
AOF 重写和 RDB 创建快照一样,都巧妙地利用了写时复制机制:
- Redis 执行 fork() ,现在同时拥有父进程和子进程。
- 子进程开始将新 AOF 文件的内容写入到临时文件。
- 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
- 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。
- 搞定!现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。
- 注意:在将内存缓存中的数据追加到新AOF文件末尾和rename时,主进程是阻塞的。
AOF文件损坏修复:
服务器可能在程序正在对 AOF 文件进行写入时停机, 如果停机造成了 AOF 文件出错(corrupt), 那么 Redis 在重启时会拒绝载入这个 AOF 文件, 从而确保数据的一致性不会被破坏。当发生这种情况时, 可以用以下方法来修复出错的 AOF 文件:
- 为现有的 AOF 文件创建一个备份。
-
使用 Redis 附带的 redis-check-aof 程序,对原来的 AOF 文件进行修复:
$ redis-check-aof –fix
- (可选)使用 diff -u 对比修复后的 AOF 文件和原始 AOF 文件的备份,查看两个文件之间的不同之处。
- 重启 Redis 服务器,等待服务器载入修复后的 AOF 文件,并进行数据恢复。
RDB数据恢复的优势:
RDB的启动时间会更短,因为RDB文件非常紧凑,RDB文件中每一条数据只有一条记录,不会像AOF日志那样可能有一条数据的多次操作记录。所以每条数据只需要写一次就行了。
RDB文件的存储格式和Redis数据在内存中的编码格式是一致的,不需要再进行数据编码工作,所以在CPU消耗上要远小于AOF日志的加载。
RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作(AOF方式在子进程重写文件的时候,父进程仍然需要向现有的AOF文件追加,并且同时还得把命令写入重写缓冲区),所以RDB持久化方式可以最大化redis的性能.
fork会使内存增大2倍:
在进行快照的时候(save),fork出来进行dump操作的子进程会占用与父进程一样的内存,真正的copy-on-write,对性能的影响和内存的耗用都是比较大的。比如机器8G内存,Redis已经使用了6G内存,这时save的话会再生成6G,变成12G,大于系统的8G。这时候会发生交换;要是虚拟内存不够则会崩溃,导致数据丢失。
通常的设计思路是利用Replication机制来弥补aof、snapshot性能上的不足,达到了数据可持久化。即Master上Snapshot和AOF都不做,来保证Master的读写性能,而Slave上则同时开启Snapshot和AOF来进行持久化,保证数据的安全性。
上边的说法有待确认:
fork后,父子进程共享相同的内存,直到父进程或者子进程对内存进行了写入之后,对被写内存的共享才会结束,所以fork时内存不是增大两倍,而是不变,只有父进程中收到修改命令的内存才会结束共享。
RDB到AOF的切换(在参考的第三篇文章中的代码示例中讲了两种切换的方法,必须看):
在 Redis 2.2 或以上版本,可以在不重启的情况下,从 RDB 切换到 AOF :
-
为最新的 dump.rdb 文件创建一个备份。
-
将备份放到一个安全的地方。
-
执行以下两条命令:
-
redis-cli config set appendonly yes
-
redis-cli config set save “”
-
确保写命令会被正确地追加到 AOF 文件的末尾。
-
执行的第一条命令开启了 AOF 功能: Redis 会阻塞直到初始 AOF 文件创建完成为止, 之后 Redis 会继续处理命令请求, 并开始将写入命令追加到 AOF 文件末尾。
执行的第二条命令用于关闭 RDB 功能。 这一步是可选的, 如果你愿意的话, 也可以同时使用 RDB 和 AOF 这两种持久化功能。
重要:别忘了在 redis.conf 中打开 AOF 功能! 否则的话, 服务器重启之后, 之前通过 CONFIG SET 设置的配置就会被遗忘, 程序会按原来的配置来启动服务器。
参考:
redis中文网对持久化介绍:http://www.redis.cn/topics/persistence.html
redis设计与实现中讲解AOF一章: http://redisbook.readthedocs.org/en/latest/internal/aof.html
一篇对持久化的实践的文章,包含测试代码,代码中的问题很有意义:http://www.chawenti.com/articles/14593.html