Redis提供了两种不一样的持久化方法来将数据存储到硬盘里面。一种方法叫快照(snapshotting),它能够将存在于某一时刻的全部数据都写入硬盘里面。另外一种方法叫只追加文件(append-onlyfile,AOF),它会在执行写命令时,将被执行的写命令复制到硬盘里面。这两种持久化方法既能够同时使用,又能够单独使用,在某些状况下甚至能够两种方法都不使用,具体选择哪一种持久化方法须要根据用户的数据以及应用来决定。数据库
将内存中的数据存储到硬盘的一个主要缘由是为了在以后重用数据,或者是为了防止系统故障而将数据备份到一个远程位置。另外,存储在Redis里面的数据有多是通过长时间计算得出的,或者有程序正在使用Redis存储的数据进行计算,因此用户会但愿本身能够将这些数据存储起来以便以后使用,这样就没必要再从新计算了。对于一些Redis应用来讲,“计算”可能只是简单地将另外一个数据库的数据复制到Redis里面,但对于另一些Redis应用来讲,Redis存储的数据多是根据数十亿行日志进行聚合分析得出的结果。编程
两组不一样的配置选项控制着Redis将数据写入硬盘里面的方式,代码清单4-1展现了这些配置选项以及它们的示例配置值。安全
代码清单4-1最开头的几个选项和快照持久化有关,好比:如何命名硬盘上的快照文件、多久执行一次自动快照操做、是否对快照文件进行压缩,以及在建立快照失败后是否仍然继续执行写命令。代码清单的第二组选项用于配置AOF子系统(subsystem):这些选项告诉Redis是否使用AOF持久化、多久才将写入的内容同步到硬盘、在对AOF进行压缩(compaction)的时候可否执行同步操做,以及多久执行一次AOF压缩。服务器
Redis 能够经过建立快照来得到存储在内存里面的数据在某个时间点上的副本。在建立快照 以后,用户能够对快照进行备份,能够将快照复制到其余服务器从而建立具备相同数据的服务器副本,还能够将快照留在原地以便重启服务器时使用。app
根据配置,快照将被写入dbfilename选项指定的文件里面,并储存在dir选项指定的路径上面。若是在新的快照文件建立完毕以前,Redis、系统或者硬件这三者之中的任意一个崩溃了,那么Redis将丢失最近一次建立快照以后写入的全部数据。编程语言
举个例子,假设Redis目前在内存里面存储了10GB的数据,上一个快照是在下午2:35开始建立的,而且已经建立成功。下午3:06时,Redis又开始建立新的快照,而且在下午3:08快照文件建立完毕以前,有35个键进行了更新。若是在下午3:06至下午3:08期间,系统发生崩溃,致使Redis没法完成新快照的建立工做,那么Redis将丢失下午2:35以后写入的全部数据。另外一方面,若是系统刚好在新的快照文件建立完毕以后崩溃,那么Redis将只丢失35个键的更新数据。性能
建立快照的办法有如下几种。学习
在只使用快照持久化来保存数据时,必定要记住:若是系统真的发生崩溃,用户将丢失最近一次生成快照以后更改的全部数据。所以,快照持久化只适用于那些即便丢失一部分数据也不会形成问题的应用程序,而不能接受这种数据损失的应用程序则能够考虑使用AOF持久化。大数据
接下来将展现几个使用快照持久化的场景,你们能够从中学习到如何经过修改配置来得到本身想要的快照持久化行为。 云计算
在我的开发服务器上面,主要考虑的是尽量地下降快照持久化带来的资源消耗。基于这个缘由以及对本身硬件的信任,我只设置了save 90 01这一条规则。其中save选项告知Redis,它应该根据这个选项提供的两个值来执行BGSAVE操做。在这个规则设置下,若是服务器距离上次成功生成快照已经超过了900秒(也就是15分钟),而且在此期间执行了至少一次写入操做,那么Redis就会自动开始一次新的BGSAVE操做。
若是你打算在生产服务器中使用快照持久化并存储大量数据,那么你的开发服务器最好可以运行在与生产服务器相同或者类似的硬件上面,并在这两个服务器上使用相同的save选项、存储类似的数据集并处理相近的负载量。把开发环境设置得尽可能贴近生产环境,有助于判断快照是否生成得过于频繁或者过于稀少(过于频繁会浪费资源,而过于稀少则带有丢失大量数据的隐患)。
当Redis存储的数据量只有几个GB的时候,使用快照来保存数据是没有问题的。Redis会建立子进程并将数据保存到硬盘里面,生成快照所需的时间比你读这句话所需的时间还要短。但随着Redis占用的内存愈来愈多,BGSAVE在建立子进程时耗费的时间也会愈来愈多。若是Redis的内存占用量达到几十个GB,而且剩余的空闲内存并很少,或者Redis运行在虚拟机(virtual machine)上面,那么执行BGSAVE可能会致使系统长时间地停顿,也可能引起系统大量地使用虚拟内存(virtual memory),从而致使Redis的性能下降至没法使用的程度。
执行BGSAVE而致使的停顿时间有多长取决于Redis所在的系统:对于真实的硬件、VMWare虚拟机或者KVM虚拟机来讲,Redis进程每占用一个GB的内存,建立该进程的子进程所需的时间就要增长10~20毫秒;而对于Xen虚拟机来讲,根据配置的不一样,Redis进程每占用一个GB的内存,建立该进程的子进程所需的时间就要增长200~300毫秒。所以,若是咱们的Redis进程占用了20GB的内存,那么在标准硬件上运行BGSAVE所建立的子进程将致使Redis停顿200~400毫秒;若是咱们使用的是Xen虚拟机(亚马逊EC2和其余几个云计算供应商都使用这种虚拟机),那么相同的建立子进程操做将致使Redis停顿4~6秒。用户必须考虑本身的应用程序可否接受这种停顿。
为了防止Redis由于建立子进程而出现停顿,咱们能够考虑关闭自动保存,转而经过手动发送BGSAVE或者SAVE来进行持久化。手动发送BGSAVE同样会引发停顿,惟一不一样的是用户能够经过手动发送BGSAVE命令来控制停顿出现的时间。另外一方面,虽然SAVE会一直阻塞Redis直到快照生成完毕,可是由于它不须要建立子进程,因此就不会像BGSAVE同样由于建立子进程而致使Redis停顿;而且由于没有子进程在争抢资源,因此SAVE建立快照的速度会比BGSAVE建立快照的速度要来得更快一些。
根据我的经验,在一台拥有68GB内存的Xen虚拟机上面,对一个占用50GB内存的Redis服务器执行BGSAVE命令的话,光是建立子进程就须要花费15秒以上,而生成快照则须要花费15~20分钟;但使用SAVE只须要3~5分钟就能够完成快照的生成工做。由于个人应用程序只须要天天生成一次快照,因此我写了一个脚本,让它在天天凌晨3点中止全部客户端对Redis的访问,调用SAVE命令并等待该命令执行完毕,以后备份刚刚生成的快照文件,并通知客户端继续执行操做。
若是用户可以妥善地处理快照持久化可能会带来的大量数据丢失,那么快照持久化对用户来讲将是一个不错的选择,但对于不少应用程序来讲,丢失15分钟、1小时甚至更长时间的数据都是不可接受的,在这种状况下,咱们可使用AOF持久化来将存储在内存里面的数据尽快地保存到硬盘里面。
简单来讲,AOF持久化会将被执行的写命令写到AOF文件的末尾,以此来记录数据发生的变化。所以,Redis只要从头至尾从新执行一次AOF文件包含的全部写命令,就能够恢复AOF文件所记录的数据集。AOF持久化能够经过设置代码清单4-1所示的appendonly yes配置选项来打开。表4-1展现了appendfsync配置选项对AOF文件的同步频率的影响。
文件同步 在向硬盘写入文件时,至少会发生3件事。当调用file.write()方法(或者其余编程语言里面的相似操做)对文件进行写入时,写入的内容首先会被存储到缓冲区,而后操做系统会在未来的某个时候将缓冲区存储的内容写入硬盘,而数据只有在被写入硬盘以后,才算是真正地保存到了硬盘里面。用户能够经过调用file.flush()方法来请求操做系统尽快地将缓冲区存储的数据写入硬盘里,但具体什么时候执行写入操做仍然由操做系统决定。除此以外,用户还能够命令操做系统将文件同步(sync)到硬盘,同步操做会一直阻塞直到指定的文件被写入硬盘为止。当同步操做执行完毕以后,即便系统出现故障也不会对被同步的文件形成任何影响。
若是用户使用appendfsync always选项的话,那么每一个Redis写命令都会被写入硬盘,从而将发生系统崩溃时出现的数据丢失减到最少。不过遗憾的是,由于这种同步策略须要对硬盘进行大量写入,因此Redis处理命令的速度会受到硬盘性能的限制:转盘式硬盘(spinning disk)在这种同步频率下每秒只能处理大约200个写命令,而固态硬盘(solid-state drive,SSD)每秒大概也只能处理几万个写命令。
警告:固态硬盘和appendfsync always使用:固态硬盘的用户请谨慎使用appendfsync always选项,由于这个选项让Redis每次只写入一个命令,而不是像其余appendfsync选项那样一次写入多个命令,这种不断地写入少许数据的作法有可能会引起严重的写入放大(write amplification)问题,在某些状况下甚至会将固态硬盘的寿命从原来的几年下降为几个月。
为了兼顾数据安全和写入性能,用户能够考虑使用appendfsynceverysec选项,让Redis以每秒一次的频率对AOF文件进行同步。Redis每秒同步一次AOF文件时的性能和不使用任何持久化特性时的性能相差无几,而经过每秒同步一次AOF文件,Redis能够保证,即便出现系统崩溃,用户也最多只会丢失一秒以内产生的数据。当硬盘忙于执行写入操做的时候,Redis还会优雅地放慢本身的速度以便适应硬盘的最大写入速度。
最后,若是用户使用appendfsync no选项,那么Redis将不对AOF文件执行任何显式的同步操做,而是由操做系统来决定应该在什么时候对AOF文件进行同步。这个选项在通常状况下不会对Redis的性能带来影响,但系统崩溃将致使使用这种选项的Redis服务器丢失不定数量的数据。另外,若是用户的硬盘处理写入操做的速度不够快的话,那么当缓冲区被等待写入硬盘的数据填满时,Redis的写入操做将被阻塞,并致使Redis处理命令请求的速度变慢。由于这个缘由,通常来讲并不推荐使用appendfsync no选项,在这里介绍它只是为了完整列举appendfsync选项可用的3个值。
虽然AOF持久化很是灵活地提供了多种不一样的选项来知足不一样应用程序对数据安全的不一样要求,但AOF持久化也有缺陷—那就是AOF文件的体积大小。
在阅读了上一节对AOF持久化的介绍以后,读者可能会感到疑惑:AOF持久化既能够将丢失数据的时间窗口下降至1秒(甚至不丢失任何数据),又能够在极短的时间内完成按期的持久化操做,那么咱们有什么理由不使用AOF持久化呢?可是这个问题实际上并无那么简单,由于Redis会不断地将被执行的写命令记录到AOF文件里面,因此随着Redis不断运行,AOF文件的体积也会不断增加,在极端状况下,体积不断增大的AOF文件甚至可能会用完硬盘的全部可用空间。还有另外一个问题就是,由于Redis在重启以后须要经过从新执行AOF文件记录的全部写命令来还原数据集,因此若是AOF文件的体积很是大,那么还原操做执行的时间就可能会很是长。
为了解决AOF文件体积不断增大的问题,用户能够向Redis发送BGREWRITEAOF命令,这个命令会经过移除AOF文件中的冗余命令来重写(rewrite)AOF文件,使AOF文件的体积变得尽量地小。BGREWRITEAOF的工做原理和BGSAVE建立快照的工做原理很是类似:Redis会建立一个子进程,而后由子进程负责对AOF文件进行重写。由于AOF文件重写也须要用到子进程,因此快照持久化由于建立子进程而致使的性能问题和内存占用问题,在AOF持久化中也一样存在。更糟糕的是,若是不加以控制的话,AOF文件的体积可能会比快照文件的体积大好几倍,在进行AOF重写并删除旧AOF文件的时候,删除一个体积达到数十GB大的旧AOF文件可能会致使操做系统挂起(hang)数秒。
跟快照持久化能够经过设置save选项来自动执行BGSAVE同样,AOF持久化也能够经过设置auto-aof-rewrite-percentage选项和auto-aof-rewrite-min-size选项来自动执行BGREWRITEAOF。举个例子,假设用户对Redis设置了配置选项auto-aof-rewrite-percentage 100和auto-aof-rewrite-min-size 64mb,而且启用了AOF持久化,那么当AOF文件的体积大于64MB,而且AOF文件的体积比上一次重写以后的体积大了至少一倍(100%)的时候,Redis将执行BGREWRITEAOF命令。若是AOF重写执行得过于频繁的话,用户能够考虑将auto-aof-rewrite-percentage选项的值设置为100以上,这种作法可让Redis在AOF文件的体积变得更大以后才执行重写操做,不过也会让Redis在启动时还原数据集所需的时间变得更长。
不管是使用AOF持久化仍是快照持久化,将数据持久化到硬盘上都是很是有必要的,但除了进行持久化以外,用户还必须对持久化所得的文件进行备份(最好是备份到多个不一样的地方),这样才能尽可能避免数据丢失事故发生。若是条件容许的话,最好能将快照文件和最新重写的AOF文件备份到不一样的服务器上面。
经过使用AOF持久化或者快照持久化,用户能够在系统重启或者崩溃的状况下仍然保留数据。随着负载量的上升,或者数据的完整性变得愈来愈重要时,用户可能须要使用复制特性。
黄健宏:<Redis实战>