Redis持久化

1.概述

redis支持将内存中的数据周期性的写入磁盘或者把操做追加到记录文件中,这个过程称为redis的持久化。redis支持两种方式的持久化,一种是快照方式(snapshotting),也称RDB方式;两一种是追加文件方式(append-only file),也称AOF方式。RDB方式是redis默认的持久化方式。持久化能够避免因进程退出而形成数据丢失。html

Redis持久化文件加载流程ios

 

2.RDB持久化

(1)RDB文件的建立

RDB持久化是把当前进程数据以二进制的方式生成快照(.rdb)文件保存到硬盘的过程,有手动触发和自动触发,手动触发有两个命令来生成RDB文件,一个是SAVE,另外一个是BGSAVE。redis

a.手动触发

SAVE命令:阻塞Redis服务器进程,在阻塞期间,服务器不能处理任何命令请求,直到RDB文件建立完毕为止,。数据库

BGSAVE命令:Redis进程执行fork操做建立子线程,由子线程负责建立RDB文件完成持久化,服务器进程(父进程)继续处理命令请求。当BGSAVE命令在执行期间,客户端再发送BGSAVE命令会被服务器拒绝,由于同时执行两个GBSAVE命令也会产生竞争条件。最后BGREWRITEAOF和GBSAVE两个命令也不能同时执行。若是没有开启AOF持久化,自动执行BGSAVE;显然BGSAVE是对SAVE的优化。下图是SAVE命令和BGSAVE命令的对比:
缓存

命令 SAVE BGSAVE
IO类型 同步 异步
是否阻塞 阻塞 非阻塞(在Fork是阻塞)
复杂度 O(n) O(n)
优势 不会消耗额外内存 不阻塞客户端命令
缺点 阻塞客户端命令 须要Fork子进程,内存开销大

 

BGSAVE运行流程安全

 

b.自动触发

 

在redis.config配置文件里能够配置自动触发,配置方式以下:服务器

 

            save <seconds> <changes>             

这个配置的规则指的是在seconds秒内发生changes次写操做,就会自动进行一次bgsave,例如:网络

 

            save 900 1                               

指的是若是900秒内有1条Key信息发生变化就会触发一次bgsave。app

 

还有在执行shutdown命令的时候,若是没有开启AOF持久化功能,那么会自动执行一次bgsave。异步

 

(2)RDB文件载入

和建立文件不一样,RDB文件的载入是在服务器启动时自动执行的,并无用于载入RDB文件的命令,只要Redis服务器在启动时检测到RDB文件的存在,它就会自动载入RDB文件。能过启动时日志记录能够查看。须要注意的是,若是打开了AOF持久化,那么服务器会优先使用AOF文件来还原数据库状态。

(3)数据集保存

a.自动间隔性保存

 

文件的建立除了SAVE和GBSAVE保存RDB 文件,还能够经过配置SAVE选项,让服务器每隔一段时间自动执行一次BGSAVE命令。能够配置SAVE选项设置多个保存条件,只要任意一个条件被知足,服务器就会执行BGSAVE命令。

 

    --默认配置的SAVE选项,保存方式有三种条件,知足任意一种就能够,以下:
    127.0.0.1:6379> config get save
 1) "save" 2) "900 1 300 10 60 10000"

 

    -- 服务器在900秒以内,对数据库进行了至少1次修改,则发起保存快照。

 

    -- 服务器在300秒以内,对数据库进行了至少10次修改,则发起保存快照。

 

    -- 服务器在60秒以内,对数据库进行了至少10000次修改,则发起保存快照。

 

b.检查保存条件是否知足

Redis的服务器周期性操做默认每隔100毫秒就会检查执行一次,用于对正在运行的服务器进行维护,其中一项工做是检查save 选项所设置的保存条件是否已经知足,若是知足就调用BGSAVE命令。

(4)Redis实现快照的过程

-  Redis使用fork函数复制一份当前进程(父进程)的副本(子进程);
-  父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件;
-  当子进程写入完全部数据后会用该临时文件替换旧的RDB文件,至此一次快照操做完成。
-  在执行fork的时候操做系统(类Unix操做系统)会使用写时复制(copy-on-write)策略,即fork函数发生的一刻父子进程共享同一内存数据,当父进程要更改其中某片数据时(如执行一个写命令 ),操做系统会将该片数据复制一份以保证子进程的数据不受影响,因此新的RDB文件存储的是执行fork一刻的内存数据。

(5)RDB持久化的优缺点

优势:

  1.只有一个文件dump.rdb,方便持久化。

  2.容灾性好,一个文件能够保存到安全的磁盘。

  3.性能最大化,fork子进程来完成写操做,让主进程继续处理命令,因此是IO最大化。

  4.相对于数据集大时,比AOF的启动效率更高。

缺点:

  1.数据安全性低,经过配置save参数来达到定时的写快照,好比 每900 秒有1个键被修改就进行一次快照,每600秒至少有10个键被修改进行快照,每30秒有至少10000个键被修改进行记录。因此若是当服务器还在等待写快照时出现了宕机,那么将会丢失数据。

  2.fork子进程时可能致使服务器停机1秒,数据集太大。

3.AOF持久化

AOF(append only file),以日志的方式记录每次写命令,服务重启的时候从新执行AOF文件中的命令来恢复内存数据。由于解决了数据持久化实时性的问题,因此目前AOF是Redis持久化的主流方式。

(1)AOF持久化流程

 

  • 全部的写命令都会追加到aof_buf(缓冲区)中。
  • 可使用不一样的策略将AOF缓冲区中的命令写到AOF文件中。
  • 随着AOF文件的愈来愈大,会对AOF文件进行重写。
  • 当服务器重启的时候,会加载AOF文件并执行AOF文件中的命令用于恢复数据。

(2)AOF持久化的实现

a.命令追加

 

当AOF执行处于打开状态时,服务器在执行完一个写命令以后,会以协议格式将被执行的写命令追加到服务器状态的AOF_buf缓冲区的末尾。

 

复制代码
    struct redisServer {

        sds aof_buf; /* AOF buffer, written before entering the event loop */
        }
复制代码

 

b.AOF文件写入与同步

edis的服务器进程就是一个事件循环(loop),这个循环中的文件事件负责接收客户端的命令请求,以及向客户端发送命令回复。在服务器每次结束一个事件循环以前,都会调用内部flushAppendOnlyFile函数,考虑是否须要将aof_buf缓冲区中的内容写入和保存到AOF文件里面。flushAppendOnlyFile函数的行为由服务器配置appendfsync选项的值来决定。appendfsync是指数据同步到磁盘文件(AOF)的方式,默认配置是everysec选项。当同步频率是everysec值时,而且距离上次同步AOF文件已经超过一秒时,那么服务器会先将aof_buf中的内容写入到AOF文件中。

 127.0.0.1:6379>config get Appendfsync
    1) "appendfsync"
    2) "everysec"

 

RDB方式是周期性的持久化数据, 若是未到持久化时间点,Redis 由于某些缘由而形成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。因此从redis 1.1开始引入了AOF方式,AOF 持久化记录服务器执行的全部写操做命令,并在服务器启动时,经过从新执行这些命令来还原数据集。 AOF 文件中的命令所有以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。

 

AOF方式仍然有丢失数据的可能,由于收到写命令后可能并不会立刻将写命令写入磁盘,所以咱们能够修改redis.conf,配置redis调用write函数写入命令到文件中的时机。以下

#启用AOF方式

appendonly yes

#每次有新命令追加到 AOF 文件时就执行一次 fsync :很是慢,也很是安全

appendfsync always

#每秒 fsync 一次:足够快(和使用 RDB 持久化差很少),而且在故障时只会丢失 1 秒钟的数据

appendfsync everysec

#从不 fsync :将数据交给操做系统来处理。更快,也更不安全的选择

appendfsync no

 

Appendfsync模式

对应flushAppendOnlyFile函数行为

no

当设置appendfsync为no的时候,Redis不会主动调用fsync去将AOF日志内容同步到磁盘,因此这一切就彻底依赖于操做系统的调试了。对大多数Linux操做系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。从效率上讲该模式最快,

但同步到磁盘不及时,是最不安全的选择。

Everysec (推荐)

当设置appendfsync为everysec的时候,Redis会默认每隔一秒进行一次fsync调用,将缓冲区中的数据写到磁盘,从效率上讲该模式足够快(和使用 RDB 持久化差很少),而且当出现故障停机时,数据库也只丢失一秒钟的命令。

always

当设置appendfsync为always时,每一次写操做都会调用一次fsync,这时数据是最安全的,固然,因为每次都会执行fsync,因此其性能也会受到影响,效率上讲该模式最慢的。

 

 

 

(3)AOF文件载入与数据还原

服务器读入并从新执行一遍aof文件里面保存的写命令,就能够还原服务器关闭以前的数据库状态。

服务器读取aof文件并还原数据库状态的流程:

a.建立一个伪客户端(由于Redis的命令只能在客户端上下文中执行),用于载入AOF文件时所使用的命令直接来源于AOF文件,而不是来自网络链接的命令。

b. 从AOF文件中分析并读出一条写命令。

c. 使用伪客户端执行被读出的写命令。

d. 重复执行步骤2和3,直到AOF文件中的全部命令都被处理完为止。

好比:服务器首先读入并执行select 0 命令,以后是set msg hello命令,再以后是sadd fruits apple banana cherry命令等等,这些命令都执行完以后,服务器的数据库就被还原到以前的状态了。

(4)AOF文件重写

AOF持久化是经过保存被执行的写命令来记录数据库状态的,随着时间流逝,Redis服务器执行的写命令愈来愈多,AOF文件也会愈来愈大;过大的AOF文件不只会影响服务器的正常运行,也会致使数据恢复须要的时间过长。为了解决AOF文件体积膨胀的问题,Redis提供了AOF文件重写功能。经过该功能,Redis服务器能够建立一个新的AOF文件来替代现有的AOF文件,新旧两个AOF文件所保存的数据库状态相同,但新的AOF文件不会包含任何浪费空间的冗余命令,因此新AOF文件体积比旧的AOF文件体积要小得多。

文件重写是指按期重写AOF文件,减少AOF文件的体积。须要注意的是,AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF文件;不会对旧的AOF文件进行任何读取、写入操做!这个重写功能是经过读取服务器当前的数据库状态来实现的。好比:使用写命令 rpush list "A", rpush list "B", rpush list "C", rpush list "D" 此时必须在AOF文件中写入四条命令。重写能够是直接从数据库中读取键list的值,而后用一条rpush list "A","B","C","D"命令来代替。

文件重写之因此可以压缩AOF文件,缘由在于: 
a.过时的数据再也不写入文件 
b.无效的命令再也不写入文件:若有些数据被重复设值(set mykey v1, set mykey v2)、有些数据被删除了(sadd myset v1, del myset)等等 
c.多条命令能够合并为一个:如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。

(5)文件重写的触发

 

文件重写的触发,分为手动触发和自动触发: 
手动触发:直接调用bgrewriteaof命令,该命令的执行与bgsave有些相似:都是fork子进程进行具体的工做,且都只有在fork时阻塞。

 

自动触发:根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数,以及aof_current_size和aof_base_size状态肯定触发时机。 
auto-aof-rewrite-min-size:执行AOF重写时,文件的最小体积,默认值为64MB。 
auto-aof-rewrite-percentage:执行AOF重写时,当前AOF大小(即aof_current_size)和上一次重写时AOF大小(aof_base_size)的比值。 
其中,参数能够经过config get命令查看: 

状态能够经过info persistence查看: 

只有当auto-aof-rewrite-min-size和auto-aof-rewrite-percentage两个参数同时知足时,才会自动触发AOF重写,即bgrewriteaof操做。

对照上图,文件重写的流程以下:

1) Redis父进程首先判断当前是否存在正在执行 bgsave/bgrewriteaof的子进程,若是存在则bgrewriteaof命令直接返回,若是存在bgsave命令则等bgsave执行完成后再执行。前面曾介绍过,这个主要是基于性能方面的考虑。 
2) 父进程执行fork操做建立子进程,这个过程当中父进程是阻塞的。 
3.1) 父进程fork后,bgrewriteaof命令返回”Background append only file rewrite started”信息并再也不阻塞父进程,并能够响应其余命令。Redis的全部写命令依然写入AOF缓冲区,并根据appendfsync策略同步到硬盘,保证原有AOF机制的正确。 
3.2) 因为fork操做使用写时复制技术,子进程只能共享fork操做时的内存数据。因为父进程依然在响应命令,所以Redis使用AOF重写缓冲区(图中的aof_rewrite_buf)保存这部分数据,防止新AOF文件生成期间丢失这部分数据。也就是说,bgrewriteaof执行期间,Redis的写命令同时追加到aof_buf和aof_rewirte_buf两个缓冲区。 
4) 子进程根据内存快照,按照命令合并规则写入到新的AOF文件。 
5.1) 子进程写完新的AOF文件后,向父进程发信号,父进程更新统计信息,具体能够经过info persistence查看。 
5.2) 父进程把AOF重写缓冲区的数据写入到新的AOF文件,这样就保证了新AOF文件所保存的数据库状态和服务器当前状态一致。 
5.3) 使用新的AOF文件替换老文件,完成AOF重写。

 

(6)AOF后台重写

 

重写做为一种辅助维护,Redis不但愿AOF重写形成服务器没法处理请求,因此Redis决定将AOF重写程序放到子进程里执行。对AOF 文件进行重写,执行bgrewriteaof命令, Redis将生成一个新的 AOF 文件,这个文件包含重写当前数据集所需的最少命令。bgrewriteaof后台重写实现步骤以下:

 

a.Redis执行 fork() ,如今同时拥有父进程和子进程。

 

b.子进程开始将新 AOF 文件的内容写入到临时文件。

 

c.对于全部新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样样即便在重写的中途发生停机,现有的 AOF 文件也仍是安全的。

 

d.当子进程完成重写工做时,它给父进程发送一个信号,父进程在接收到信号以后,将内存缓存中的全部数据追加到新 AOF 文件的末尾。

 

e.如今Redis原子地用新文件替换旧文件,以后全部命令都会直接追加到新 AOF 文件的末尾。

 

(7)AOF配置详解

 

appendonly yes     //启用aof持久化方式

 

# appendfsync always //每收到写命令就当即强制写入磁盘,最慢的,可是保证彻底的持久化,不推荐使用

 

appendfsync everysec //每秒强制写入磁盘一次,性能和持久化方面作了折中,推荐

 

# appendfsync no    //彻底依赖os,性能最好,持久化没保证(操做系统自身的同步)

 

no-appendfsync-on-rewrite  yes  //正在导出rdb快照的过程当中,要不要中止同步aof

 

auto-aof-rewrite-percentage 100  //aof文件大小比起上次重写时的大小,增加率100%时,重写

 

auto-aof-rewrite-min-size 64mb   //aof文件,至少超过64M时,重写

具体配置相关项参照如下表格

选项

取值

说明

Appendonly

no|yes

是否开启AOF机制

Appendfilename

"appendonly.aof"

Aof文件名

Appendfsync

no|appendfsync|always

AOF持久化同步频率

no-appendfsync-on-rewrite

No | yes

在日志进行BGREWRITEAOF时,若是设置为yes表示新写操做不进行同步fsync,只是暂存在缓冲区里,避免形成磁盘IO操做冲突,等重写完成后在写入。redis中默认为no 

 

auto-aof-rewrite-percentage

100

当前AOF文件大小是上第二天志重写时的AOF文件大小两倍时,发生BGREWRITEAOF操做。

auto-aof-rewrite-min-size

64mb

当前AOF文件执行BGREWRITEAOF命令的最小值,避免刚开始启动Reids时因为文件尺寸较小致使频繁的BGREWRITEAOF。

aof-load-truncated

yes

Redis再恢复时,忽略最后一条可能存在问题的指令

aof-use-rdb-preamble

no

新增RDB-AOF混合持久化格式,在开启了这个功能以后,AOF重写产生的文件将同时包含RDB格式的内容和AOF格式的内容

 

(8)AOF持久化的优缺点

优势

1). 该机制能够带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不一样步。事实上,每秒同步也是异步完成的,其效率也是很是高的,所差的是一旦系统出现宕机现象,那么这一秒钟以内修改的数据将会丢失。而每修改同步,咱们能够将其视为同步持久化,即每次发生的数据变化都会被当即记录到磁盘中。能够预见,这种方式在效率上是最低的。至于无同步,无需多言,我想你们都能正确的理解它。

2). 因为该机制对日志文件的写入操做采用的是append模式,所以在写入过程当中即便出现宕机现象,也不会破坏日志文件中已经存在的内容。然而若是咱们本次操做只是写入了一半数据就出现了系统崩溃问题,不用担忧,在Redis下一次启动以前,咱们能够经过redis-check-aof工具来帮助咱们解决数据一致性的问题。

3). 若是日志过大,Redis能够自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会建立一个新的文件用于记录此期间有哪些修改命令被执行。所以在进行rewrite切换时能够更好的保证数据安全性。

4). AOF包含一个格式清晰、易于理解的日志文件用于记录全部的修改操做。事实上,咱们也能够经过该文件完成数据的重建。

缺点

1). 对于相同数量的数据集而言,AOF文件一般要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

2). 根据同步策略的不一样,AOF在运行效率上每每会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB同样高效。

4.RDB与AOF二者的区别

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操做过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换以前的文件,用二进制压缩存储。

 

AOF持久化以日志的形式记录服务器所处理的每个写、删除操做,查询操做不会记录,以文本的方式记录,能够打开文件看到详细的操做记录。

 

 

              RDB AOF
存储数据   

保存键空间的全部键值对(包括过时字典中的数据),并以二进制形式保存,符合rdb文件规范,根据不一样数据类型会有不一样处理

 保存redis服务器所执行的全部写命令来记录数据库状态,在写入以前命令存储在aof_buf缓冲区。

 持久化时间选择

 经过conf的save选项设置持久化行为(单位时间内的修改次数)。

 经过conf的appendfsync选项设置持久化行为
数据还原

服务器载入rdb文件,阻塞线程,在载入完成以前不接受任何命令。

服务器建立不带网络链接的伪客户端,读取aof文件中的全部命令并执行(redis服务开启aof持久化在服务器启动时会选择aof文件恢复数据库状态)
 过时键  在写入或读取时会忽略过时键  不会忽略过时键,在键被惰性删除或按期删除时向aof文件追加一条删除命令
 文件大小  随着存储数据量的变化而变化(根据不一样数据类型有不一样的数据压缩优化)  随着执行命令的增长而增长(经过aof重写进行优化)
执行命令 SAVE和BGSAVE --

5.持久化相关阻塞

(1)fork阻塞:CPU的阻塞

在Redis中,不管是RDB持久化的bgsave,仍是AOF重写的bgrewriteaof,都须要fork出子进程来进行操做。若是Redis内存过大,会致使fork操做时复制内存页表耗时过多;而Redis主进程在进行fork时,是彻底阻塞的,也就意味着没法响应客户端的请求,会形成请求延迟过大。 
对于不一样的硬件、不一样的操做系统,fork操做的耗时会有所差异,通常来讲,若是Redis单机内存达到了10GB,fork时耗时可能会达到百毫秒级别(若是使用Xen虚拟机,这个耗时可能达到秒级别)。所以,通常来讲Redis单机内存通常要限制在10GB之内;不过这个数据并非绝对的,能够经过观察线上环境fork的耗时来进行调整。观察的方法以下:执行命令info stats,查看latest_fork_usec的值,单位为微秒。 

为了减轻fork操做带来的阻塞问题,除了控制Redis单机内存的大小之外,还能够适度放宽AOF重写的触发条件、选用物理机或高效支持fork操做的虚拟化技术等,例如使用Vmware或KVM虚拟机,不要使用Xen虚拟机。

(2)AOF追加阻塞:硬盘的阻塞

 

当开启aof持久化功能时,文件刷盘的方式通常采用每秒一次,后台线程每秒对aof文件作fsync操做,硬盘压力过大时,fsync操做须要等待,直到写入完成若是主线程发现距离上一次的fsync成功超过2秒,为了数据安全性它会阻塞直到后台线程执行fsync操做完成,这种阻塞行为主要是硬盘压力引发,可查看info persistence统计中的aof_delayed_fsync指标,每次发生aof刷盘阻塞主线程时会累加。

AOF追加阻塞问题定位的方法: 
(1)监控info Persistence中的aof_delayed_fsync:当AOF追加阻塞发生时(即主线程等待fsync而阻塞),该指标累加。 
(2)AOF阻塞时的Redis日志: 
Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis. 
(3)若是AOF追加阻塞频繁发生,说明系统的硬盘负载太大;能够考虑更换IO速度更快的硬盘,或者经过IO监控分析工具对系统的IO负载进行分析,如iostat(系统级io)、iotop(io版的top)、pidstat等。

 

 

6.总结

本文主要内容能够总结以下: 
一、持久化在Redis高可用中的做用:数据备份,与主从复制相比强调的是由内存到硬盘的备份。 
二、RDB持久化:将数据快照备份到硬盘;介绍了其触发条件(包括手动出发和自动触发)、执行流程、RDB文件等,特别须要注意的是文件保存操做由fork出的子进程来进行。 
三、AOF持久化:将执行的写命令备份到硬盘(相似于MySQL的binlog),介绍了其开启方法、执行流程等,特别须要注意的是文件同步策略的选择(everysec)、文件重写的流程。 
四、一些现实的问题:包括如何选择持久化策略,以及须要注意的fork阻塞、AOF追加阻塞等。

7.参考资料

RDB参考

https://www.cnblogs.com/bamaofan/p/5284014.html

https://www.cnblogs.com/MrHSR/p/9999957.html

https://www.cnblogs.com/hjwublog/p/5660578.html

https://www.cnblogs.com/leeSmall/p/8379768.html

https://www.cnblogs.com/kevingrace/p/5685332.html

AOF参考

https://blog.csdn.net/Leon_cx/article/details/81545178

https://blog.csdn.net/qq_35433716/article/details/82195106

https://blog.csdn.net/hangbo216/article/details/68925644

https://www.cnblogs.com/MrHSR/P/10045533.html

https://www.cnblogs.com/chenliangcl/p/7240350.html

https://www.cnblogs.com/xingzc/p/5988080.html

https://www.cnblogs.com/jieliu726/p/9045942.html

https://blog.csdn.net/fjj15732621696/article/details/81842075

https://blog.csdn.net/linbiaorui/article/details/79822318

相关文章
相关标签/搜索