Redis-持久化
Redis是一个键值对数据库服务器,服务器中通常包含着任意个非空数据库,而每个非空数据库中又可以包含任意个键值对,为了方便起见,我们将服务器中的非空数据库以及它们的键值对统称为数据库状态。
Redis是内存数据库,它将自己的数据库状态存储在内存里面,如果服务器进程退出,那么服务器中的数据库状态也会消失不见。为了解决这个问题,Redis提供了持久化功能,这个功能可以将Redis在内存中的数据库状态保存到磁盘里面,避免数据意外丢失。
RDB持久化
RDB(快照)持久化既可以手动执行,也可以根据服务器配置选择定期执行,该功能可以将某个时间点上的数据库状态保存到一个RDB文件中。
RDB持久化生成的RDB文件是一个经过压缩的二进制文件,可以使用这个文件还原生成RDB时的数据库状态。
RDB文件的创建与载入
SAVE、BGSAVE都可以用于生成RDB文件。
SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕,在服务器进程阻塞期间,服务器不能处理任何命令请求。
BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求。
RDB文件的载入工作实在服务器启动时自动执行的。因为AOF文件的更新频率通常比RDB文件的更新频率高,所以:
- 如果服务器开启了AOF持久化功能,那么服务期会优先使用AOF文件来还原数据库状态;
- 如果AOF为关闭状态,则使用RDB还原数据库状态。
服务器在RDB载入期间,会一直处于阻塞状态,直到载入工作完成为止。
自动间隔性保存
SAVE命令由服务器进程执行保存工作,BGSAVE命令则由子进程执行保存工作。因为BGSAVE不阻塞服务器进程,所以Redis允许用户通过设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令。
例如下列配置:1
2
3save 900 1
save 300 10
save 600 10000
上述第一个参数值为时间间隔,第二个值为操作次数。也就是说在900秒内有一次以上修改,就进行保存。条件之间为或关系。
服务器结构中有一个saveparam属性保存保存条件,还维持了一个dirty计数器和lastsave属性;dirty保存上次保存命令后执行的修改次数,lastsave保存上次保存命令的时间,服务器通过对比这两个属性和saveparam中的值来决定是否进行保存指令。
RDB文件结构
REDIS | db_version | database | EOF | check_sum |
---|---|---|---|---|
database 部分包含了零个或多个数据库,以及各个数据库中的键值对数据。
check_sum是一个8字节长的无符号整数,保存着一个校验和,载入时会检查计算的检验和和这个属性是否相等,以此检查文件是否损坏。
RDB文件中保存数据库每个键值对的过期时间(如果有的话)、类型、键、值。
优点
- RDB文件是一个很简洁的单文件,它保存了某个时间点的Redis数据,很适合用于做备份。你可以设定一个时间点对RDB文件进行归档,这样就能在需要的时候很轻易的把数据恢复到不同的版本。
- 基于上面所描述的特性,RDB很适合用于灾备。单文件很方便就能传输到远程的服务器上。
- RDB的性能很好,需要进行持久化时,主进程会fork一个子进程出来,然后把持久化的工作交给子进程,自己不会有相关的I/O操作。
- 比起AOF,在数据量比较大的情况下,RDB的启动速度更快。
缺点
- RDB容易造成数据的丢失。假设每5分钟保存一次快照,如果Redis因为某些原因不能正常工作,那么从上次产生快照到Redis出现问题这段时间的数据就会丢失了。
- RDB使用fork()产生子进程进行数据的持久化,如果数据比较大的话可能就会花费点时间,造成Redis停止服务几毫秒。如果数据量很大且CPU性能不是很好的时候,停止服务的时间甚至会到1秒。
AOF持久化
AOF(命令追加)持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的。
AOF持久化的实现
AOF持久化功能的实现可分为命令追加、文件写入、文件同步三个步骤。
当AOF是打开状态时,服务器在执行完一个写命令之后,会一写一个时将这条命令追加到服务器状态的aof_buf缓冲区的末尾。这一步骤即为命令追加。
在计算机中,为了提高效率,写入操作通常不会直接写到磁盘中,而是写到内存的缓冲区中,等到缓冲区满或者超过指定时限后才将缓冲区中的内容写入磁盘。Redis中同样如此,因此使用一个属性appendfsync来描述将缓冲区中内容同步到AOF文件的时限。
当appendfsync的值为always时,服务器的每个事件都要将aof_buf中的内容写入AOF文件,因此最为安全,但效率也最低。
当appendfsync的值为everysec时,每个事件循环完成后,每隔一秒就将缓冲区中内容同步至AOF文件。
当appendfsync为no时,何时同步由操作系统控制。
AOF的载入与数据还原
AOF中保存了重建数据库状态所需的所有写命令,所以只要重新执行一遍就可还原数据库状态。具体步骤如下:
- 创建一个不带网络连接的伪客户端
- 从AOF中分析并读取一条写命令
- 使用伪客户端执行被读出的写命令。
- 重复执行知道处理完
AOF重写
为了防止AOF文件由于时间流逝而保存了越来越多的内容,Redis提供了重写功能。通过该功能,Redis可以创建一个新的AOF文件来代替现有文件,新旧文件保存的数据库状态相同,但新的AOF文件不会包含冗余命令,所以体积会小许多。
AOF实现重写的原理是首先从数据库中读取现在的值,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令。
AOF重写程序在子进程中执行,这样做有两个好处:
- 子进程重写期间,父进程可以处理命令请求
- 子进程带有服务进程的数据副本,使用子进程而不是线程,可以避免使用锁的情况下,保证数据安全性
在子进程重写期间,服务进程还需继续处理命令请求,新的命令可能会对现有的数据库状态进行修改,从而使得数据状态不一致。为了解决问这个问题,Redis服务器设置了一个AOF重写缓冲区,这个缓冲区在服务器创建子进程之后开始使用,当Redis服务器执行完一个写命令后,他会同事讲这个写命令发送到AOF缓冲区和AOF重写缓冲区。这样可以保证:
- AOF缓冲区的内容会定期被写入和同步到AOF文件
- 从创建子进程开始,服务器执行的所有写命令都会记录到AOF重写缓冲区里面。
当重写完成后,它会向父进程发送一个信号,父进程在接收到该信号之后,会调用一个信号处理函数,并执行以下工作:
- 将AOF重写缓冲区中的所有内容写入新的AOF文件中
- 对新的AOF文件进行改名,原子地覆盖现有的AOF文件,完成替换。
优点
- 比RDB可靠。你可以制定不同的fsync策略:不进行fsync、每秒fsync一次和每次查询进行fsync。默认是每秒fsync一次。这意味着你最多丢失一秒钟的数据。
- AOF日志文件是一个纯追加的文件。就算是遇到突然停电的情况,也不会出现日志的定位或者损坏问题。甚至如果因为某些原因(例如磁盘满了)命令只写了一半到日志文件里,我们也可以用redis-check-aof这个工具很简单的进行修复。
- 当AOF文件太大时,Redis会自动在后台进行重写。重写很安全,因为重写是在一个新的文件上进行,同时Redis会继续往旧的文件追加数据。新文件上会写入能重建当前数据集的最小操作命令的集合。当新文件重写完,Redis会把新旧文件进行切换,然后开始把数据写到新文件上。
- AOF把操作命令以简单易懂的格式一条接一条的保存在文件里,很容易导出来用于恢复数据。例如我们不小心用FLUSHALL命令把所有数据刷掉了,只要文件没有被重写,我们可以把服务停掉,把最后那条命令删掉,然后重启服务,这样就能把被刷掉的数据恢复回来。
缺点
- 在相同的数据集下,AOF文件的大小一般会比RDB文件大。
- 在某些fsync策略下,AOF的速度会比RDB慢。通常fsync设置为每秒一次就能获得比较高的性能,而在禁止fsync的情况下速度可以达到RDB的水平。
- 在过去曾经发现一些很罕见的BUG导致使用AOF重建的数据跟原数据不一致的问题。