写点什么

redis 系列之——数据持久化(RDB 和 AOF)

用户头像
诸葛小猿
关注
发布于: 2020 年 07 月 18 日
redis系列之——数据持久化(RDB和AOF)

Redis系列目录



redis系列之——分布式锁



redis系列之——缓存穿透、缓存击穿、缓存雪崩



redis系列之——Redis为什么这么快?



redis系列之——数据持久化(RDB和AOF)



redis系列之——一致性hash算法



redis系列之——高可用(主从、哨兵、集群)



redis系列之——事物及乐观锁



redis系列之——数据类型geospatial:你隔壁有没有老王?



redis系列之——数据类型bitmaps:今天你签到了吗?



布隆过滤器是个啥!



在数据库(如mysql)和缓存(如redis)的发展中,都会相互借鉴对方的长处来弥补自身的不足。比如mysql作为持久化数据库,为了提高数据的访问速度,会使用缓存技术,当一条sql查询完成后,mysql会使用sql生成一个key,并将这个sql查询的结果缓存到这个key上,如果运行相同的sql,服务器直接从缓存中去获取结果,就不需要再去解析、优化、执行sql了。同时,redis作为缓存,为了解决宕机带了的数据丢失问题,也增加了持久化机制。



Redis支持RDB和AOF两种持久化机制,持久化功能有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复,理解掌握持久化机制对于Redis运维非常重要。



今天我们就聊一聊redis的持久化问题。本期也是面试高频问题,同时也是实际生产必须考虑的问题。



一、RDB(Redis DataBase)



默认情况下,只开启RDB。RDB是二进制文件。



原理



RDB方式也叫快照方式,这种方式会在一定的触发时机下,将当前redis的内存快照保存到磁盘上的dump.rdb文件中。这个过程中,主要执行一个命令bgsave





  1. 在一定的条件下触发bgsave执行



  1. Redis父进程判断当前是否存在正在执行的子进程,如RDB/AOF子进程,如果存在bgsave命令直接返回。



  1. 父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞,不能响应其他客户端请求。通过info stats命令查看latest_fork_usec选项,可以获取最近一个fork操作的耗时,单位为微秒。Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。



  1. 父进程fork完成后,bgsave命令返回“Background saving started”信息并不再阻塞父进程,可以继续响应其他客户端命令。



  1. 子进程创建RDB文件dump.rdb,根据父进程内存生成临时快RDB文件。

  2. 使用临时RDB文件对原有RDB文件的原子替换。执行lastsave命令可以获取最后一次生成RDB的时间,对应info统计的rdb_last_save_time选项

  3. 子进程退出。



配置



################################ SNAPSHOTTING ################################
#900s内,如果至少有一个1key进行了修改,就进行持久化操作
save 900 1
#300s内,如果至少有一个10key进行了修改,就进行持久化操作
save 300 10
#60s内,如果至少有一个10000key进行了修改,就进行持久化操作
save 60 10000
#持久化如果出错,是否还需要继续工作
stop-writes-on-bgsave-error yes
#是否压缩rdb文件,需要消耗一些cpu资源
rdbcompression yes
#保存rdb文件的时候,进行错误的检查校验
rdbchecksum yes
#持久化的文件名字
dbfilename dump.rdb
#rdb文件保存的目录
dir ./



触发机制



触发RDB持久化过程分为手动触发自动触发



手动触发



通过redis-cli登录redis后,可以使用savebgsave两个命令触发RDB持久化。



  • save命令:阻塞当前Redis服务器,直到RDB过程完成为止,对于内存比较大的实例会造成长时间阻塞,线上环境不建议使用。



  • bgsave命令:Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。



显然bgsave命令是针对save阻塞问题做的优化。因此Redis内部所有的涉及RDB的操作都采用bgsave的方式,而save命令已经废弃。



自动触发



除了执行命令手动触发之外,Redis内部还存在自动触发RDB的持久化机制。



  • 满足上面配置文件中save m n的触发条件时,redis会自动执行bgsave

  • slave全量复制master时,会发信息给master,这是master会执行bgsave,执行完成之后会将rdb文件发送给slave。

  • 执行shutdownflushall命令时,redis会自动执行bgsave



可以在redis-cli的命令行执行config set save " "关闭RDB的持久化机制。



数据恢复



rdb文件会自动恢复。在redis客户端命令行通过config get dir获取 redis 的安装目录,将备份文件 (dump.rdb) 移动到 安装目录并启动服务即可,redis就会自动加载文件数据至内存了。



Redis启动后会读取RDB快照文件,将数据从硬盘载入到内存。根据数据量大小与结构和服务器性能不同,这个时间也不同。RDB本身是二进制文件,恢复非常快。通常将一个记录一千万个字符串类型键、大小为1GB的快照文件载入到内存中需要花费20~30秒钟。



127.0.0.1:6379> config get dir #获取redis的目录
1) "dir"
2) "/usr/local/bin" # 将dump.rdb文件放在这个目录下,重新启动redis,就可以自动将数据恢复



优缺点



优点



代表Redis在某个时间点上的数据快照。非常适用于备份,全量复制等场景。比如每6小时执行bgsave备份,并把RDB文件拷贝到远程机器或者文件系统中(如hdfs),用于灾难恢复(对数据完整性和一致性要求不高)



RDB是二进制保存,Redis加载RDB恢复数据远远快于AOF的方式(适合大规模的数据恢复)



缺点



RDB方式没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作(内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑),频繁执行成本过高(影响性能)



RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题(版本不兼容)



在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。



二、AOF(Append Only File)



默认情况下,aof是关闭的。AOF命令写入的内容直接是文本协议格式,可以通过vim查看文件内容。



原理



以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式。掌握AOF持久化机制,对兼顾数据安全性和性能非常有帮助。通俗一点的理解就是以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。



aof的持久化有三步:命令写入(append)、文件同步(sync)、文件重写(rewrite)。





  1. 所有的写入命令会追加到aof_buf(缓冲区)中。

  2. AOF缓冲区根据对应的策略向硬盘做同步操作。

  3. 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。



Rewrite原理 : AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename)。遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。



配置



############################## APPEND ONLY MODE ###############################
# 默认不开启aop持久化机制,因为RDB对于一般的业务已经够用了
appendonly no
# aof持久化文件名称
appendfilename "appendonly.aof"
# 不执行sync, 完全依赖操作系统的刷写,一般30秒一次。性能最好,但是持久化没有保障,不推荐
# appendfsync no
# 每次有写命令执行,就会执行一次sync,将命令追加到aof日志文件。保证完全持久化,不会丢数据,性能也是最差的,不推荐
# appendfsync always
# 每秒钟执行一次sync,将命令追加到aof日志文件。在性能和持久化之间做了折中,推荐。也是默认配置
appendfsync everysec
# 默认不会对aof文件使用BGREWRITEAOF命令进行重写(重写会合并命令,减小aof文件占用的空间)
no-appendfsync-on-rewrite no
# 当aof文件占用的空间超过上一次记录的100%时,并且aof文件每增加64M,就会执行重写(两个条件同时满足)
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 加载aof文件时,如果文件本身有问题(如文件结尾),是否继续加载。yes:继续加载,no:加载出现错误,停止加载,需要修复aof文件后才能加载,这是不能重启redis
aof-load-truncated yes



这里需要注意的是触发sync的配置和rewrite的配置。



触发机制



AOF的appendfsync触发机制是上面配置的三个参数决定的:no、always、everysec。可以根据对性能和持久化的实时性要求,具体配置。如果不知道哪种合适,就使用默认的everysec,可能会有1s的数据丢失。



aof文件的重写过程的触发,由上面配置的rewrite参数决定。可以手动触发和自动触发。



手动触发rewrite



通过redis-cli登录redis后,可以使用bgrewriteaof命令触发aof的重写机制。



自动动触发rewrite



根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机,当两个条件同时满足时,就会触发重写。



数据恢复





redis启动时,首先判断aof功能是否开启,如果开启并且存在aof持久化文件,则只会使用aof文件进行数据恢复,而不会使用rdb文件进行数据恢复。



使用aof文件恢复时,AOF文件可能存在结尾不完整的情况,比如机器突然掉电导致AOF尾部文件命令写入不全。Redis为我们提供了aof-load-truncated配置来兼容这种情况,默认开启。加载AOF时,当遇到此问题时会忽略并继续启动。



如果关闭aof-load-truncated配置,当aof文件不完整时,则redis会启动失败,这时使用客户端连接也是失败的:



[root@redis ~]# redis-cli -p 6379
Could not connect to Redis at 127.0.0.1:6379: Connection refused
not connected> exit
[root@redis ~]#



对于错误格式的AOF文件,可以先进行备份,然后采用redis安装目录下的redis-check-aof命令修复aof文件,修复后使用linux的命令diff -u对比数据的差异,找出丢失的数据,有些可以人工修改补全。



[root@redis ~]# redis-check-aof --fix appendonly.aof
0x a4: Expected \n\r, got:6461
AOF analyzed: size=185, ok_up_to=139, diff=46
This will shrink the AOF form 185 bytes, with 46 bytes, to 139 bytes
Continue? [y/N]: y # 输入y,同意修改
Successfully truncated AOF
[root@redis ~]#



优缺点



优点



  1. 每一次修改都同步,文件完整性更高。

  2. 可以根据具体业务,选择同步的参数,可以兼顾性能和同步的实时性。每一秒同步一次,可只会丢失1秒钟的数据。



缺点



  1. AOF文件远大于RDB文件,数据恢复速度比rdb慢。



三、总结



1、RDB持久化方式能够在指定的时间间隔内对数据进行快照存储,默认开启。



2、AOF持久化方式记录每次对服务器写操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据。AOF持久化会追加保存每次写的操作到文件末尾, Redis还能对AOF文件进行后台重写(rewiter),使得AOF文件的体积不至于过大。默认不开启。



3、如果每次重启机器,都有单独的服务将mysql中的数据加载到redis中,也可以不使用任何持久化。



4、同时开启两种持久化方式



  • 在这种情况下,当 redis重启的时候,会优先载入aof文件恢复原始数据。因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集完整。

  • RDB文件的完整性没有AOF高,同时使用两者时,服务器重启也只会使用AOF文件,那要不要只使用AOF呢?建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),是二进制存储恢复快,而且不会有AOF可能潜在的Bug,留着作为一个万一的手段。



5、性能建议



  • 因为RDB文件只用作数据备份,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。

  • 如果开启aof,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单,只load自己的AOF文件就可以了,代价一是带来了持续的IO;二是 AOF rewrite的最后将 rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。应该尽量减少 AOF rewrite的频率,AOF重写的基础大小默认值64M(auto-aof-rewrite-min-size 64mb)太小了,可以设到5G以上;默认超过原大小100%大小重写可以改到适当的数值。

  • 如果不开启aof,仅靠 Master-Slave Replication实现高可用性也可以,能省掉一大笔IO,也减少了rewrite时带来的系统波动。代价是如果 Master/Slave同时宕机,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。



完成,收工!





传播知识,共享价值】,感谢小伙伴们的关注和支持,我是【诸葛小猿】,一个彷徨中奋斗的互联网民工!!!





发布于: 2020 年 07 月 18 日阅读数: 210
用户头像

诸葛小猿

关注

我是诸葛小猿,一个彷徨中奋斗的互联网民工 2020.07.08 加入

公众号:foolish_man_xl

评论 (1 条评论)

发布
用户头像
不错,挺详细的
2020 年 07 月 22 日 08:06
回复
没有更多了
redis系列之——数据持久化(RDB和AOF)