redis演练(5) redis持久化

何谓持久化,就是媳妇让你,持久一些。

说白了持久化:就是将内存中的数据保存到磁盘上的过程(数据库也算磁盘的特殊表现),以保证宕机或断电后,可以继续访问。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】

 redis演练(5) redis持久化

AOF,英文是Append Only File,即只允许追加不允许改写的文件。AOF方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行一遍,就这么简单。有点数据库软件联机日志的影子。

 redis演练(5) redis持久化

两者对比

 redis演练(5) redis持久化

redis两种持久化方案参数说明

涉及到文件保存,一般要考虑以下几个问题,不仅仅限于redis。

  • 保存时机:什么时候触发。取决于数据一致性和效率的平衡

  • 保存目标对象:日志,二进制…

  • 保存目录:本地目录,还是共享目录

  • 是否压缩:节省空间

  • 是否校验:考虑安全

  • 保存失败处理机制:异常报告

  • 开启线程数:决定速度,但会影响其他功能

  • 保存文件限制:超过大小,不允许;和不允许上传*.exe文件等

带着这些问题,去扣相关的RDB参数,和AOF参数,问题就简单多了。

RDB # 存 DB 到磁盘:
#
#   格式:save <间隔时间(秒)> <写入次数>
#
#   根据给定的时间间隔和写入次数将数据保存到磁盘
#
#   下面的例子的意思是:
#   900 秒后如果至少有 1 个 key 的值变化,则保存
#   300 秒后如果至少有 10 个 key 的值变化,则保存
#   60 秒后如果至少有 10000 个 key 的值变化,则保存
#
#   注意:你可以注释掉所有的 save 行来停用保存功能。
#   也可以直接一个空字符串来实现停用:
#   save “”

save 900 1
save 300 10
save 60 10000

# 默认情况下,如果 redis 最后一次的后台保存失败,redis 将停止接受写操作,
# 这样以一种强硬的方式让用户知道数据不能正确的持久化到磁盘,
# 否则就会没人注意到灾难的发生。
#
# 如果后台保存进程重新启动工作了,redis 也将自动的允许写操作。
#
# 然而你要是安装了靠谱的监控,你可能不希望 redis 这样做,那你就改成 no 好了。
stop-writes-on-bgsave-error yes

# 是否在 dump .rdb 数据库的时候使用 LZF 压缩字符串
# 默认都设为 yes
# 如果你希望保存子进程节省点 cpu ,你就设置它为 no ,
# 不过这个数据集可能就会比较大
rdbcompression yes

# 是否校验rdb文件
rdbchecksum yes

# 设置 dump 的文件位置
dbfilename dump.rdb

# 工作目录
# 例如上面的 dbfilename 只指定了文件名,
# 但是它会写入到这个目录下。这个配置项一定是个目录,而不能是文件名。
dir ./

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操作,  
    #在某些linux结构中redis会在调用sync()方法时阻塞很长时间。记住,现在还没办法解决这个问题,即使在不同进程中进行调用也会block。  
    #  
    #使用如下配置可能会缓解这个问题,这样会在存储大数据或者BIGREWRITEAOF的时候不会在主进程中调用fsync()方法。  
    #  
    # 这表示,如果另外一个子进程在进行保存操作,redis的表现如同配置为‘appendfsync no’。  
    #在实际应用中,这表示在最坏的情景下(使用linux默认配置)可能会丢失30秒日志。  
    #   
    #如果你有特殊的情况可以配置为’yes’。但是配置为’no’是最为安全的选择。  
    no-appendfsync-on-rewrite no  
      
      
    #自动重写只增文件。  
    #redis可以自动盲从的调用‘BGREWRITEAOF’来重写日志文件,如果日志文件增长了指定的百分比。  
    #   
    #它是这样工作的:每次rewrite后redis会记录日志文件的大小。(如果重启后没有重写后的大小,就默认用日志文件大小)  
    #  
    # 这个基准日志大小和当前日志大小做比较。如果当前大小比指定的百分比,重写机制就会被触发。  
    #同时,你也要制定一个重写下线,用来避免增长百分比够了,但是日志文件还很小的情况。  
    #  
    #指定百分比为0可以注掉自动重写日志文件功能。       
    auto-aof-rewrite-percentage 100  

    auto-aof-rewrite-min-size 64mb

#redis在启动的时候可以加载被截断的AOF文件,默认启用;    (3.0以后才支持)

aof-load-truncated yes
    


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,键值丢失”

[root@hadoop2 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
[root@hadoop2 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
[root@hadoop2 redis]# kill -9 2546
#重启redis
[root@hadoop2 redis]# bin/redis-server  redis.conf 
[root@hadoop2 redis]# bin/redis-cli  -h  192.168.163.156
# blog键值丢失
192.168.163.156:6379> get blog
(nil)

这个结果,是不是非常出人意外。看来“江湖上都传言,redis断电键值不丢失”有点问题。

其实,没问题。是上面的演练存在不足,数据量没有达到触发时机要求。继续。

2.2演练“重启redis,键值不丢失”

[root@hadoop2 redis]# bin/redis-cli  -h  192.168.163.156
192.168.163.156:6379> set blog "blog.51cto.com"
OK
#借助自带测试工具,发送1w请求,触发RDB保存
[root@hadoop2 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
...
# 确认  键值还存在
[root@hadoop2 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服务
[root@hadoop2 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
[root@hadoop2 redis]# kill -9 3241
#重启redis服务
[root@hadoop2 redis]# bin/redis-server  redis.conf 
#确认重启后,blog键值依然存在
[root@hadoop2 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
[root@hadoop2 redis]# pkill redis
#重启
[root@hadoop2 redis]# bin/redis-server  redis.conf 
[root@hadoop2 redis]# bin/redis-cli  -h  192.168.163.156
#键值未丢失
192.168.163.156:6379> get blog
"blog.51cto.com"

结论:托AOF的福,键值没有丢失,和RDB不同。原因是两者的触发时机不同。

这时候,确认下生产AOF文件

#仅仅存了一条记录
[root@hadoop2 redis]# ll
-rw-r--r--. 1 root root    67 9月   3 10:31 appendonly.aof
#查看日志内容(为文本文件)
[root@hadoop2 redis]# cat appendonly.aof 
*2
$6
SELECT
$1
0
*3
$3
set
$4
blog
$14
blog.51cto.com

3.2演练重启AOF重写效果

通过查看日志内容,可以确认存放的是操作命令日志。这势必导致文件大小增长过快,这和RDB文件不同。RDB存储的是二进制文件,而且是某时刻的快照,存储的本身就是面向内存结果。稍后会提供例子演示下RDB的内容。

准备测试数据

[root@hadoop2 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日志
[root@hadoop2 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文件重写
[root@hadoop2 redis]# bin/redis-benchmark  -r 20000 -h 192.168.163.156

#appendonly.aof文件变化过程(11M->27M -->32M->33M->42M->8.5M.
...
[root@hadoop2 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
[root@hadoop2 redis]# ll appendonly.aof  -h
-rw-r--r--. 1 root root 32M 9月   3 11:04 appendonly.aof
[root@hadoop2 redis]# ll appendonly.aof  -h
-rw-r--r--. 1 root root 33M 9月   3 11:04 appendonly.aof
[root@hadoop2 redis]# ll appendonly.aof  -h
-rw-r--r--. 1 root root 36M 9月   3 11:04 appendonly.aof
[root@hadoop2 redis]# ll appendonly.aof  -h
-rw-r--r--. 1 root root 42M 9月   3 11:04 appendonly.aof
[root@hadoop2 redis]# ll appendonly.aof  -h
-rw-r--r--. 1 root root 44M 9月   3 11:04 appendonly.aof
[root@hadoop2 redis]# ll appendonly.aof  -h
-rw-r--r--. 1 root root 8.5M 9月   3 11:04 appendonly.aof
[root@hadoop2 redis]# ll appendonly.aof  -h
-rw-r--r--. 1 root root 11M 9月   3 11:04 appendonly.aof
[root@hadoop2 redis]# ll appendonly.aof  -h
-rw-r--r--. 1 root root 15M 9月   3 11:04 appendonly.aof

从42M 突然变成了8.5M,明显发生了AOF重写操作。

以year为目标,确认下AOF重写发生了什么。

 redis演练(5) redis持久化

重写前后,发现将多次操作的结果,转换为一个等价的命令,大大降低了存储空间。

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条请求
[root@hadoop2 redis]# bin/redis-benchmark  -r 10000 -h 192.168.163.156
#4. 比较2个文件大小
[root@hadoop2 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文件损坏

[root@hadoop2 redis]# bin/redis-server  redis.conf 
[root@hadoop2 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演练(5) redis持久化

使用redis-check-aof 验证下,果然不出所料“文件有问题”

[root@hadoop2 redis]# bin/redis-check-aof 
Usage: bin/redis-check-aof [--fix] <file.aof>
[root@hadoop2 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>

客户端连接失败

[root@hadoop2 redis]# bin/redis-cli  -h 192.168.163.156
Could not connect to Redis at 192.168.163.156:6379: Connection refused

修复aof文件

[root@hadoop2 redis]# bin/redis-check-aof
Usage: bin/redis-check-aof [–fix] <file.aof>
[root@hadoop2 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

[root@hadoop2 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向“存储”靠近了一大步,数据可靠性提供了保证。

友情提示,演练文章,最好配合官方理论说明性文档一同观看,效果显著。

原创文章,作者:3628473679,如若转载,请注明出处:https://blog.ytso.com/197202.html

(0)
上一篇 2021年11月17日
下一篇 2021年11月17日

相关推荐

发表回复

登录后才能评论