何谓持久化,就是媳妇让你,持久一些。
说白了持久化:就是将内存中的数据保存到磁盘上的过程(数据库也算磁盘的特殊表现),以保证宕机或断电后,可以继续访问。java中常见的持久化框架,如Hibernate,ibatis,jdbc都是持久化实现方式的一种,当然普通文件保存功能也算。
拿memcached来说,memcached保存的信息,没有进行持久化,所以只能存活一个进程生命期,下次重新启动,数据全部丢失。这算memcached的一个缺点吧。redis提供了持久化支持,而且提供了2种持久化方案。
《redis演练系列》,立足于案例展示,通过形象,对比,图示的方式,达到知识梳理过程。
本章的主要梗概。
- redis两种持久化方案对比
- redis两种持久化方案参数说明
- 演练RDB的触发时机
- 演练AOF
- RDB VS AOF
1.redis两种持久化方案对比
redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。RDB,AOF就相当于redis持久界的2个亲兄弟,相互配合,相互提携。当然也会争抢些资源。
RDB,简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;
AOF,则是换了一个角度来实现持久化,那就是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。
RDB将服务器包含的所有数据库数据以二进制文件的形式保存到硬盘里面。
下图,是具体操作步骤。具体详情参见【http://www.cnblogs.com/luogankun/p/3986403.html】
AOF,英文是Append Only File,即只允许追加不允许改写的文件。AOF方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行一遍,就这么简单。有点数据库软件联机日志的影子。
两者对比
redis两种持久化方案参数说明
涉及到文件保存,一般要考虑以下几个问题,不仅仅限于redis。
- 保存时机:什么时候触发。取决于数据一致性和效率的平衡
- 保存目标对象:日志,二进制...
- 保存目录:本地目录,还是共享目录
- 是否压缩:节省空间
- 是否校验:考虑安全
- 保存失败处理机制:异常报告
- 开启线程数:决定速度,但会影响其他功能
- 保存文件限制:超过大小,不允许;和不允许上传*.exe文件等
带着这些问题,去扣相关的RDB参数,和AOF参数,问题就简单多了。
RDB | # 存 DB 到磁盘: # # 格式:save <间隔时间(秒)> <写入次数> # # 根据给定的时间间隔和写入次数将数据保存到磁盘 # # 下面的例子的意思是: # 900 秒后如果至少有 1 个 key 的值变化,则保存 # 300 秒后如果至少有 10 个 key 的值变化,则保存 # 60 秒后如果至少有 10000 个 key 的值变化,则保存 # # 注意:你可以注释掉所有的 save 行来停用保存功能。 # 也可以直接一个空字符串来实现停用: # save "" save 900 1 # 默认情况下,如果 redis 最后一次的后台保存失败,redis 将停止接受写操作, # 是否在 dump .rdb 数据库的时候使用 LZF 压缩字符串 # 是否校验rdb文件 # 设置 dump 的文件位置 # 工作目录 |
AOF | #默认情况下Redis会异步的将数据导出到磁盘上。这种模式对许多应用程序已经足够了, #但是如果断电或者redis进程出问题就会导致一段时间内的更新数据丢失(取决与配置项) # #这种只增文件是可选的能够提供更好的体验的数据持久化策略。 #举个例子,如果使用默认的配置数据fsync策略,在服务器意外断电的情况下redis只会丢失一秒中内的更新数据, #或者当redis进程出问题但操作系统运转正常时,redis只会丢失一个数据更新操作。 # #AOF 和 RDB 持久化方式可以同时启动并且无冲突。 #如果AOF开启,启动redis时会加载aof文件,这些文件能够提供更好的保证。 #请在 http://redis.io/topics/persistence 获取更多数据持久化信息。 appendonly no # 只增文件的文件名称。(默认是appendonly.aof) # appendfilename appendonly.aof #调用fsync()函数会通知操作系统真正将数据写入磁盘,而不是等待缓冲区中有更多数据。 #有些操作系统会将数据输出到磁盘,有些操作系统只是ASAP。 # #redis支持三种不同的方式: # #no:不调用,之等待操作系统来清空缓冲区当操作系统要输出数据时。很快。 # always: 每次更新数据都写入仅增日志文件。慢,但是最安全。 # everysec: 每秒调用一次。折中。 # #默认是每秒中一次,因为它往往是在速度和数据安全两者之间的折中选择。 #如果你可以接受让操作系统去自动清空缓存,你可以将这项配置降低到‘no‘(如果你可以接受一段时间的数据丢失,默认的rdb就足够了), #这完全取决与你。如果你想要一个更好的体验或者从相反的角度,使用‘always‘,这样会很慢,但是比‘everysec‘安全些。 # #请在下面的文章中获取更多细节知识: # http://antirez.com/post/redis-persistence-demystified.html # #如果你不是很清楚这三项之间的区别,或者不知道哪种适合你的机器,就是用默认吧。 # appendfsync always appendfsync everysec # appendfsync no #当AOF策略设置为‘always‘或者‘everysec‘的时候,后台的保存进程会进行很多磁盘I/O操作, auto-aof-rewrite-min-size 64mb #redis在启动的时候可以加载被截断的AOF文件,默认启用; (3.0以后才支持)
|
2.演练RDB的触发时机
# 900 秒后如果至少有 1 个 key 的值变化,则保存
# 300 秒后如果至少有 10 个 key 的值变化,则保存
# 60 秒后如果至少有 10000 个 key 的值变化,则保存
# 注意:你可以注释掉所有的 save 行来停用保存功能。
# 也可以直接一个空字符串来实现停用:
# save ""
save 900 1
save 300 10
save 60 10000
采用默认的RDB配置
redis.conf
save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes dbfilename dump.rdb dir ./
2.1演练“重启redis,键值丢失”
[[email protected] redis]# bin/redis-cli -h 192.168.163.156 192.168.163.156:6379> flushdb OK 192.168.163.156:6379> set blog "blog.51cto.com" OK #为键赋值 192.168.163.156:6379> get blog "blog.51cto.com" 192.168.163.156:6379> exit [[email protected] redis]# ps -ef |grep redis root 2546 1 0 07:42 ? 00:00:05 /usr/local/redis/bin/redis-server 192.168.163.156:6379 root 3235 2517 0 08:54 pts/0 00:00:00 grep redis [[email protected] redis]# kill -9 2546 #重启redis [[email protected] redis]# bin/redis-server redis.conf [[email protected] redis]# bin/redis-cli -h 192.168.163.156 # blog键值丢失 192.168.163.156:6379> get blog (nil)
这个结果,是不是非常出人意外。看来“江湖上都传言,redis断电键值不丢失”有点问题。
其实,没问题。是上面的演练存在不足,数据量没有达到触发时机要求。继续。
2.2演练“重启redis,键值不丢失”
[[email protected] redis]# bin/redis-cli -h 192.168.163.156 192.168.163.156:6379> set blog "blog.51cto.com" OK #借助自带测试工具,发送1w请求,触发RDB保存 [[email protected] redis]# bin/redis-benchmark -r 10000 -h 192.168.163.156 ====== PING_INLINE ====== 100000 requests completed in 0.83 seconds 50 parallel clients 3 bytes payload keep alive: 1 99.77% <= 1 milliseconds ... # 确认 键值还存在 [[email protected] redis]# bin/redis-cli -h 192.168.163.156 192.168.163.156:6379> get blog "blog.51cto.com" 192.168.163.156:6379> exit #关闭 redis服务 [[email protected] redis]# ps -ef |grep redis root 3241 1 3 08:54 ? 00:00:19 bin/redis-server 192.168.163.156:6379 root 3351 2517 0 09:05 pts/0 00:00:00 grep redis [[email protected] redis]# kill -9 3241 #重启redis服务 [[email protected] redis]# bin/redis-server redis.conf #确认重启后,blog键值依然存在 [[email protected] redis]# bin/redis-cli -h 192.168.163.156 192.168.163.156:6379> get blog "blog.51cto.com"
看来“江湖上都传言,redis断电键值不丢失”,是有前提条件的。
3.演练AOF
修改redis.conf 开启AOF 关闭RDB
save 900 1 save 300 10 save 60 10000 save "" stop-writes-on-bgsave-error yes rdbcompression yes dbfilename dump.rdb dir ./ appendonly yes appendfilename "appendonly.aof" appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 16mb aof-load-truncated yes
3.1演练重启redis,键值是否丢失
192.168.163.156:6379> set blog "blog.51cto.com" OK 192.168.163.156:6379> exit [[email protected] redis]# pkill redis #重启 [[email protected] redis]# bin/redis-server redis.conf [[email protected] redis]# bin/redis-cli -h 192.168.163.156 #键值未丢失 192.168.163.156:6379> get blog "blog.51cto.com"
结论:托AOF的福,键值没有丢失,和RDB不同。原因是两者的触发时机不同。
这时候,确认下生产AOF文件
#仅仅存了一条记录 [[email protected] redis]# ll -rw-r--r--. 1 root root 67 9月 3 10:31 appendonly.aof #查看日志内容(为文本文件) [[email protected] redis]# cat appendonly.aof *2 $6 SELECT $1 0 *3 $3 set $4 blog $14 blog.51cto.com
3.2演练重启AOF重写效果
通过查看日志内容,可以确认存放的是操作命令日志。这势必导致文件大小增长过快,这和RDB文件不同。RDB存储的是二进制文件,而且是某时刻的快照,存储的本身就是面向内存结果。稍后会提供例子演示下RDB的内容。
准备测试数据
[[email protected] redis]# bin/redis-cli -h 192.168.163.156 #顺序操作键值多次 192.168.163.156:6379> set year 2001 OK 192.168.163.156:6379> incr year (integer) 2002 192.168.163.156:6379> incr year (integer) 2003 192.168.163.156:6379> incr year (integer) 2004 192.168.163.156:6379> incr year (integer) 2005 192.168.163.156:6379> incr year (integer) 2006 192.168.163.156:6379> incr year (integer) 2007 192.168.163.156:6379> incr year (integer) 2008 192.168.163.156:6379> incr year (integer) 2009 192.168.163.156:6379> incr year (integer) 2010 192.168.163.156:6379> get year "2010" # get没有输出到AOF日志 [[email protected] redis]# cat appendonly.aof *2 $6 SELECT $1 0 *3 $3 set $4 blog $14 blog.51cto.com *2 $6 SELECT $1 0 *3 $3 set $4 year $4 2001 *2 $4 incr $4 year *2 $4 incr $4 year *2 $4 incr $4 year *2 $4 incr $4 year *2 $4 incr $4 year *2 $4 incr $4 year *2 $4 incr $4 year *2 $4 incr $4 year *2 $4 incr $4 year #接下来,借助benchmark工具,批量插入数据,触发AOF文件重写 [[email protected] redis]# bin/redis-benchmark -r 20000 -h 192.168.163.156 #appendonly.aof文件变化过程(11M->27M -->32M->33M->42M->8.5M. ... [[email protected] redis]# ll appendonly.aof -h -rw-r--r--. 1 root root 11M 9月 3 11:03 appendonly.aof -rw-r--r--. 1 root root 27M 9月 3 11:03 appendonly.aof [[email protected] redis]# ll appendonly.aof -h -rw-r--r--. 1 root root 32M 9月 3 11:04 appendonly.aof [[email protected] redis]# ll appendonly.aof -h -rw-r--r--. 1 root root 33M 9月 3 11:04 appendonly.aof [[email protected] redis]# ll appendonly.aof -h -rw-r--r--. 1 root root 36M 9月 3 11:04 appendonly.aof [[email protected] redis]# ll appendonly.aof -h -rw-r--r--. 1 root root 42M 9月 3 11:04 appendonly.aof [[email protected] redis]# ll appendonly.aof -h -rw-r--r--. 1 root root 44M 9月 3 11:04 appendonly.aof [[email protected] redis]# ll appendonly.aof -h -rw-r--r--. 1 root root 8.5M 9月 3 11:04 appendonly.aof [[email protected] redis]# ll appendonly.aof -h -rw-r--r--. 1 root root 11M 9月 3 11:04 appendonly.aof [[email protected] redis]# ll appendonly.aof -h -rw-r--r--. 1 root root 15M 9月 3 11:04 appendonly.aof
从42M 突然变成了8.5M,明显发生了AOF重写操作。
以year为目标,确认下AOF重写发生了什么。
重写前后,发现将多次操作的结果,转换为一个等价的命令,大大降低了存储空间。
1.我们也可以使用bgrewriteaof来手动触发AOF的自动重写。
2 .调用 BGSAVE 能手动触发快照保存,保存快照。
但线上环境要注意,阻塞情况。
4.AOF VS RDB.
两种持久化方式,不是水火不容,而是相互扶持,相融以沫。
官方文档,建议两者同时开启
同时开启两种持久化(redis.conf)
save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes dbfilename dump.rdb dir ./ appendonly yes appendfilename "appendonly.aof" appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 16mb aof-load-truncated yes
4.1比较2种方式的文件存储
1.删除rdb,aof文件 2.重启redis-server #3.批量发送1w条请求 [[email protected] redis]# bin/redis-benchmark -r 10000 -h 192.168.163.156 #4. 比较2个文件大小 [[email protected] redis]# ll -h 总用量 29M -rw-r--r--. 1 root root 192 9月 3 10:58 1 -rw-r--r--. 1 root root 28M 9月 3 11:26 appendonly.aof -rw-r--r--. 1 root root 457K 9月 3 11:26 dump.rdb
4.2 演练aof文件损坏
[[email protected] redis]# bin/redis-server redis.conf [[email protected] redis]# bin/redis-cli -h 192.168.163.156 192.168.163.156:6379> set blog "blog.51cto.com" OK 192.168.163.156:6379> set subject "redis" OK 192.168.163.156:6379> set year 2016 OK 192.168.163.156:6379> keys * 1) "year" 2) "subject" 3) "blog"
手动修改下appendonly.aof 文件
使用redis-check-aof 验证下,果然不出所料“文件有问题”
[[email protected] redis]# bin/redis-check-aof Usage: bin/redis-check-aof [--fix] <file.aof> [[email protected] redis]# bin/redis-check-aof appendonly.aof 0x 0: Expected \r\n, got: 0a00 AOF analyzed: size=112, ok_up_to=0, diff=112 AOF is not valid
...查看启动日志(WARRING 暂时先放着)
4690:M 03 Sep 11:42:12.213 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 4690:M 03 Sep 11:42:12.213 # Server started, Redis version 3.2.3 4690:M 03 Sep 11:42:12.213 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add ‘vm.overcommit_memory = 1‘ to /etc/sysctl.conf and then reboot or run the command ‘sysctl vm.overcommit_memory=1‘ for this to take effect. 4690:M 03 Sep 11:42:12.214 # Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>
客户端连接失败
[[email protected] redis]# bin/redis-cli -h 192.168.163.156
Could not connect to Redis at 192.168.163.156:6379: Connection refused
修复aof文件
[[email protected] redis]# bin/redis-check-aof
Usage: bin/redis-check-aof [--fix] <file.aof>
[[email protected] redis]# bin/redis-check-aof --fix appendonly.aof
0x 0: Expected \r\n, got: 0a00
AOF analyzed: size=112, ok_up_to=0, diff=112
This will shrink the AOF from 112 bytes, with 112 bytes, to 0 bytes
Continue? [y/N]: y
Successfully truncated AOF
#修复的结果是清空了AOF文件。
重启成功了,但遗憾的是数据全丢失了。
bin/redis-server redis.conf
[[email protected] redis]# cat appendonly.aof
bin/redis-cli -h 192.168.163.156
192.168.163.156:6379> keys *
(empty list or set)
当然,演示AOF文件出问题,这是个比较严重的问题。可见备份的重要性。
AOF方式的另一个好处,我们通过一个“场景再现”来说明。某同学在操作redis时,不小心执行了FLUSHALL,导致redis内存中的数据全部被清空了,这是很悲剧的事情。不过这也不是世界末日,只要redis配置了AOF持久化方式,且AOF文件还没有被重写(rewrite),我们就可以用最快的速度暂停redis并编辑AOF文件,将最后一行的FLUSHALL命令删除,然后重启redis,就可以恢复redis的所有数据到FLUSHALL之前的状态了。是不是很神奇,这就是AOF持久化方式的好处之一。但是如果AOF文件已经被重写了,那就无法通过这种方法来恢复数据了。
前人,曾静曰过的。难道有问题。于是我重新实验了下。
这次我加快了速度,立刻关闭redis服务,以及使用vi命令直接修改aof文件。结果却是可以将数据恢复。
这个演示不补贴了。
正是有了持久化,才使redis向“存储”靠近了一大步,数据可靠性提供了保证。
友情提示,演练文章,最好配合官方理论说明性文档一同观看,效果显著。