redis持久化详解RDB和AOF优缺点
本⽂将先说明上述⼏种技术分别解决了Redis⾼可⽤的什么问题;然后详细介绍Redis的持久化技术,主要是RDB和AOF两种持久化⽅案;在介绍RDB和AOF⽅案时,不仅介绍其作⽤及操作⽅法,同时介绍持久化实现的⼀些原理细节及需要注意的问题。最后,介绍在实际使⽤中,持久化⽅案的选择,以及经常遇到的问题等。
⽬录
⼀、Redis⾼可⽤概述
⼆、Redis持久化概述
三、RDB持久化
四、AOF持久化
五、⽅案选择与常见问题
六、总结
⼀、Redis⾼可⽤概述
在介绍Redis⾼可⽤之前,先说明⼀下在Redis的语境中⾼可⽤的含义。
我们知道,在web服务器中,⾼可⽤是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999% 等等)。但是在Redis语境中,⾼可⽤的含义似乎要宽泛⼀些,除了保证提供正常服务(如主从分离、快速容灾技术),还需要考虑数据容量的扩展、数据安全不会丢失等。
在Redis中,实现⾼可⽤的技术主要包括持久化、复制、哨兵和集,下⾯分别说明它们的作⽤,以及解决了什么样的问题。
1. 持久化:持久化是最简单的⾼可⽤⽅法(有时甚⾄不被归为⾼可⽤的⼿段),主要作⽤是数据备份,即将数据存储在硬盘,保证数据不会
因进程退出⽽丢失。
2. 复制:复制是⾼可⽤Redis的基础,哨兵和集都是在复制基础上实现⾼可⽤的。复制主要实现了数据的多机备份,以及对于读操作的
负载均衡和简单的故障恢复。缺陷:故障恢复⽆法⾃动化;写操作⽆法负载均衡;存储能⼒受到单机的限制。
3. 哨兵:在复制的基础上,哨兵实现了⾃动化的故障恢复。缺陷:写操作⽆法负载均衡;存储能⼒受到单机的限制。
redis支持的五种数据类型
4. 集:通过集,Redis解决了写操作⽆法负载均衡,以及存储能⼒受到单机限制的问题,实现了较为完善的⾼可⽤⽅案。
⼆、Redis持久化概述
持久化的功能:Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis重启时,利⽤持久化⽂件实现数据恢复。除此之外,为了进⾏灾难备份,可以将持久化⽂件拷贝到⼀个远程位置。
Redis持久化分为RDB持久化和AOF持久化:前者将当前数据保存到硬盘,后者则是将每次执⾏的写命令保存到硬盘(类似于MySQL的binlog);由于AOF持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此AOF是⽬前主流的持久化⽅式,不过RDB持久化仍然有其⽤武之地。
下⾯依次介绍RDB持久化和AOF持久化;由于Redis各个版本之间存在差异,如⽆特殊说明,以Redis3.0为准。
三、RDB持久化
RDB持久化是将当前进程中的数据⽣成快照保存到硬盘(因此也称作快照持久化),保存的⽂件后缀是rdb;当Redis重新启动时,可以读取快照⽂件恢复数据。
1. 触发条件
RDB持久化的触发分为⼿动触发和⾃动触发两种。
1) ⼿动触发
save命令和bgsave命令都可以⽣成RDB⽂件。
save命令会阻塞Redis服务器进程,直到RDB⽂件创建完毕为⽌,在Redis服务器阻塞期间,服务器不能处理任何命令请求。
⽽bgsave命令会创建⼀个⼦进程,由⼦进程来负责创建RDB⽂件,⽗进程(即Redis主进程)则继续处理请求。
此时服务器执⾏⽇志如下:
bgsave命令执⾏过程中,只有fork⼦进程时会阻塞服务器,⽽对于save命令,整个过程都会阻塞服务器,因此save已基本被废弃,线上环境要杜绝save的使⽤;后⽂中也将只介绍bgsave命令。此外,在⾃动触发RDB持久化时,Redis也会选择bgsave⽽不是save来进⾏持久化;下⾯介绍⾃动触发RDB持久化的条件。
2) ⾃动触发
save m n
⾃动触发最常见的情况是在配置⽂件中通过save m n,指定当m秒内发⽣n次变化时,会触发bgsave。
例如,查看redis的默认配置⽂件(Linux下为redis根⽬录下的f),可以看到如下配置信息:
其中save 900 1的含义是:当时间到900秒时,如果redis数据发⽣了⾄少1次变化,则执⾏bgsave;save 300 10和save 60 10000同理。当三个save条件满⾜任意⼀个时,都会引起bgsave的调⽤。
save m n的实现原理
Redis的save m n,是通过serverCron函数、dirty计数器、和lastsave时间戳来实现的。
serverCron是Redis服务器的周期性操作函数,默认每隔100ms执⾏⼀次;该函数对服务器的状态进⾏维护,其中⼀项⼯作就是检查save m n 配置的条件是否满⾜,如果满⾜就执⾏bgsave。
dirty计数器是Redis服务器维持的⼀个状态,记录了上⼀次执⾏bgsave/save命令后,服务器状态进⾏了多少次修改(包括增删改);⽽当save/bgsave执⾏完成后,会将dirty重新置为0。
例如,如果Redis执⾏了set mykey helloworld,则dirty值会+1;如果执⾏了sadd myset v1 v2 v3,则dirty值会+3;注意dirty记录的是服务器进⾏了多少次修改,⽽不是客户端执⾏了多少修改数据的命令。
lastsave时间戳也是Redis服务器维持的⼀个状态,记录的是上⼀次成功执⾏save/bgsave的时间。
save m n的原理如下:每隔100ms,执⾏serverCron函数;在serverCron函数中,遍历save m n配置
的保存条件,只要有⼀个条件满⾜,就进⾏bgsave。对于每⼀个save m n条件,只有下⾯两条同时满⾜时才算满⾜:
(1)当前时间-lastsave > m
(2)dirty >= n
save m n 执⾏⽇志
下图是save m n触发bgsave执⾏时,服务器打印⽇志的情况:
其他⾃动触发机制
除了save m n 以外,还有⼀些其他情况会触发bgsave:
在主从复制场景下,如果从节点执⾏全量复制操作,则主节点会执⾏bgsave命令,并将rdb⽂件发送给从节点
执⾏shutdown命令时,⾃动执⾏rdb持久化,如下图所⽰:
2. 执⾏流程
图⽚中的5个步骤所进⾏的操作如下:
1) Redis⽗进程⾸先判断:当前是否在执⾏save,或bgsave/bgrewriteaof(后⾯会详细介绍该命令)的⼦进程,如果在执⾏则bgsave命令直接返回。bgsave/bgrewriteaof 的⼦进程不能同时执⾏,主要是基于性能⽅⾯的考虑:两个并发的⼦进程同时执⾏⼤量的磁盘写操作,可能引起严重的性能问题。
2) ⽗进程执⾏fork操作创建⼦进程,这个过程中⽗进程是阻塞的,Redis不能执⾏来⾃客户端的任何命令
3) ⽗进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞⽗进程,并可以响应其他命令
4) ⼦进程创建RDB⽂件,根据⽗进程内存快照⽣成临时快照⽂件,完成后对原有⽂件进⾏原⼦替换
5) ⼦进程发送信号给⽗进程表⽰完成,⽗进程更新统计信息
3. RDB⽂件
RDB⽂件是经过压缩的⼆进制⽂件,下⾯介绍关于RDB⽂件的⼀些细节。
存储路径
RDB⽂件的存储路径既可以在启动前配置,也可以通过命令动态设定。
配置:dir配置指定⽬录,dbfilename指定⽂件名。默认是Redis根⽬录下的dump.rdb⽂件。
动态设定:Redis启动后也可以动态修改RDB存储路径,在磁盘损害或空间不⾜时⾮常有⽤;执⾏命令为config set dir {newdir}和config set dbfilename {newFileName}。如下所⽰(Windows环境):
RDB⽂件格式
RDB⽂件格式如下图所⽰(图⽚来源:《Redis设计与实现》):
其中各个字段的含义说明如下:
1) REDIS:常量,保存着”REDIS”5个字符。
2) db_version:RDB⽂件的版本号,注意不是Redis的版本号。
3) SELECTDB 0 pairs:表⽰⼀个完整的数据库(0号数据库),同理SELECTDB 3 pairs表⽰完整的3号数据库;只有当数据库中有键值对时,RDB⽂件中才会有该数据库的信息(上图所⽰的Redis中只有0号
和3号数据库有键值对);如果Redis中所有的数据库都没有键值对,则这⼀部分直接省略。其中:SELECTDB是⼀个常量,代表后⾯跟着的是数据库号码;0和3是数据库号码;pairs则存储了具体的键值对信息,包括key、value值,及其数据类型、内部编码、过期时间、压缩信息等等。
4) EOF:常量,标志RDB⽂件正⽂内容结束。
5) check_sum:前⾯所有内容的校验和;Redis在载⼊RBD⽂件时,会计算前⾯的校验和并与check_sum值⽐较,判断⽂件是否损坏。
压缩
Redis默认采⽤LZF算法对RDB⽂件进⾏压缩。虽然压缩耗时,但是可以⼤⼤减⼩RDB⽂件的体积,因此压缩默认开启;可以通过命令关闭:
需要注意的是,RDB⽂件的压缩并不是针对整个⽂件进⾏的,⽽是对数据库中的字符串进⾏的,且只有在字符串达到⼀定长度(20字节)时才会进⾏。
4. 启动时加载
RDB⽂件的载⼊⼯作是在服务器启动时⾃动执⾏的,并没有专门的命令。但是由于AOF的优先级更⾼,
因此当AOF开启时,Redis会优先载⼊AOF⽂件来恢复数据;只有当AOF关闭时,才会在Redis服务器启动时检测RDB⽂件,并⾃动载⼊。服务器载⼊RDB⽂件期间处于阻塞状态,直到载⼊完成为⽌。
Redis启动⽇志中可以看到⾃动载⼊的执⾏:
Redis载⼊RDB⽂件时,会对RDB⽂件进⾏校验,如果⽂件损坏,则⽇志中会打印错误,Redis启动失败。
5. RDB常⽤配置总结
下⾯是RDB常⽤的配置项,以及默认值;前⾯介绍过的这⾥不再详细介绍。
save m n:bgsave⾃动触发的条件;如果没有save m n配置,相当于⾃动的RDB持久化关闭,不过此时仍可以通过其他⽅式触发stop-writes-on-bgsave-error yes:当bgsave出现错误时,Redis是否停⽌执⾏写命令;设置为yes,则当硬盘出现问题时,可以及时发现,避免数据的⼤量丢失;设置为no,则Redis⽆视bgsave的错误继续执⾏写命令,当对Redis服务器的系统(尤其是硬盘)使⽤了监控时,该选项考虑设置为no
rdbcompression yes:是否开启RDB⽂件压缩
rdbchecksum yes:是否开启RDB⽂件的校验,在写⼊⽂件和读取⽂件时都起作⽤;关闭checksum在写⼊⽂件和启动⽂件时⼤约能带来10%的性能提升,但是数据损坏时⽆法发现
dbfilename dump.rdb:RDB⽂件名
dir ./:RDB⽂件和AOF⽂件所在⽬录
四、AOF持久化
RDB持久化是将进程数据写⼊⽂件,⽽AOF持久化(即Append Only File持久化),则是将Redis执⾏的每次写命令记录到单独的⽇志⽂件中(有点像MySQL的binlog);当Redis重启时再次执⾏AOF⽂件中的命令来恢复数据。
与RDB相⽐,AOF的实时性更好,因此已成为主流的持久化⽅案。
1. 开启AOF
Redis服务器默认开启RDB,关闭AOF;要开启AOF,需要在配置⽂件中配置:
appendonly yes
2. 执⾏流程
由于需要记录Redis的每条写命令,因此AOF不需要触发,下⾯介绍AOF的执⾏流程。
AOF的执⾏流程包括:
命令追加(append):将Redis的写命令追加到缓冲区aof_buf;
⽂件写⼊(write)和⽂件同步(sync):根据不同的同步策略将aof_buf中的内容同步到硬盘;
⽂件重写(rewrite):定期重写AOF⽂件,达到压缩的⽬的。
1) 命令追加(append)
Redis先将写命令追加到缓冲区,⽽不是直接写⼊⽂件,主要是为了避免每次有写命令都直接写⼊硬盘,导致硬盘IO成为Redis负载的瓶颈。
命令追加的格式是Redis命令请求的协议格式,它是⼀种纯⽂本格式,具有兼容性好、可读性强、容易处理、操作简单避免⼆次开销等优点;具体格式略。在AOF⽂件中,除了⽤于指定数据库的select命令(如select 0 为选中0号数据库)是由Redis添加的,其他都是客户端发送来的写命令。
2) ⽂件写⼊(write)和⽂件同步(sync)
Redis提供了多种AOF缓存区的同步⽂件策略,策略涉及到操作系统的write函数和fsync函数,说明如下:
为了提⾼⽂件写⼊效率,在现代操作系统中,当⽤户调⽤write函数将数据写⼊⽂件时,操作系统通常会将数据暂存到⼀个内存缓冲区⾥,当缓冲区被填满或超过了指定时限后,才真正将缓冲区的数据写⼊到硬盘⾥。这样的操作虽然提⾼了效率,但也带来了安全问题:如果计算机停机,内存缓冲区中的数据会丢失;因此系统同时提供了fsync、fdatasync等同步函数,可以强制操作系统⽴刻将缓冲区中的数据写⼊到硬盘⾥,从⽽确保数据的安全性。
AOF缓存区的同步⽂件策略由参数appendfsync控制,各个值的含义如下:
always:命令写⼊aof_buf后⽴即调⽤系统fsync操作同步到AOF⽂件,fsync完成后线程返回。这种情况下,每次有写命令都要同步到AOF⽂件,硬盘IO成为性能瓶颈,Redis只能⽀持⼤约⼏百TPS写⼊,严重降低了Redis的性能;即便是使⽤固态硬盘(SSD),每秒⼤约也只能处理⼏万个命令,⽽且会⼤⼤降低SSD的寿命。
no:命令写⼊aof_buf后调⽤系统write操作,不对AOF⽂件做fsync同步;同步由操作系统负责,通常
同步周期为30秒。这种情况下,⽂件同步的时间不可控,且缓冲区中堆积的数据会很多,数据安全性⽆法保证。
everysec:命令写⼊aof_buf后调⽤系统write操作,write完成后线程返回;fsync同步⽂件操作由专门的线程每秒调⽤⼀次。
everysec是前述两种策略的折中,是性能和数据安全性的平衡,因此是Redis的默认配置,也是我们推荐的配置。
3) ⽂件重写(rewrite)
随着时间流逝,Redis服务器执⾏的写命令越来越多,AOF⽂件也会越来越⼤;过⼤的AOF⽂件不仅会影响服务器的正常运⾏,也会导致数据恢复需要的时间过长。
⽂件重写是指定期重写AOF⽂件,减⼩AOF⽂件的体积。需要注意的是,AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF⽂件;不会对旧的AOF⽂件进⾏任何读取、写⼊操作!
关于⽂件重写需要注意的另⼀点是:对于AOF持久化来说,⽂件重写虽然是强烈推荐的,但并不是必须的;即使没有⽂件重写,数据也可以被持久化并在Redis启动的时候导⼊;因此在⼀些实现中,会关闭⾃动的⽂件重写,然后通过定时任务在每天的某⼀时刻定时执⾏。
⽂件重写之所以能够压缩AOF⽂件,原因在于:
过期的数据不再写⼊⽂件
⽆效的命令不再写⼊⽂件:如有些数据被重复设值(set mykey v1, set mykey v2)、有些数据被删除了(sadd myset v1, del myset)等等
多条命令可以合并为⼀个:如sadd myset v1, sadd myset v2, sadd myset v3可以合并为sadd myset v1 v2 v3。不过为了防⽌单条命令过⼤造成客户端缓冲区溢出,对于list、set、hash、zset类型的key,并不⼀定只使⽤⼀条命令;⽽是以某个常量为界将命令拆分为多条。这个常量在redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD中定义,不可更改,3.0版本中值是64。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。