Python--Redis实战:第四章:数据安全与性能保障:第2节:快照持久化

上一篇文章: Python--Redis实战:第四章:数据安全与性能保障:第1节:持久化选项
下一篇文章: Python--Redis实战:第四章:数据安全与性能保障:第3节:AOF持久化

Redis能够经过建立快照来得到存储在内存里面的数据在某个时间点上的副本。在建立快照以后,用户能够对快照进行备份,能够将快照复制到其它服务器从而建立具备相同数据的服务器副本,还能够将快照留在原地以便重启服务器时使用。redis

根据配置,快照将被写入dbfilename选项指定的文件里面,并存储在dir选项指定的路径上面。若是在新的快照文件建立成功以前,Redis、系统或者硬件上三者之中的任意一个崩溃了,那么Redis将丢失最近一次建立快照以后写入的全部数据。segmentfault

举个例子,假设Redis目前在内存里面存储了10GB的数据,上一个快照是在下午2:35开始建立的,而且已经建立成功。下午3:06时,Redis又开始建立新的快照,而且在下午3:08快照文件建立完毕以前,有35个键进行了更新。若是在下午3:06至3:08期间,系统发生崩溃,致使Redis没法完成新快照的建立工做,那么Redis将丢失下午2:35以后写入的全部数据。安全

建立快照的办法有如下几种:服务器

  • 客户端能够经过向Redis发送bgsave命令建立一个快照。对于支持bgsave命令的平台来讲(基本上全部的平台都支持,除了Windows平台),Redis会调用fork来建立一个子进程,而后子进程负责将快照写入硬盘,而父进程则继续处理命令请求。
fork:当一个进程建立子进程的时候,底层的操做系统会建立该进程的一个副本。在Unix和类Unix系统上面,建立子进程的操做会进行以下优化:在刚开始的时候,父进程共享相同的内存,直到父进程或者子进程对内存进行了写入以后,写入内存的共享才会结束。
  • 客户端还能够经过向Redis发singsave命令来建立一个快照,接到save命令的Redis服务器在快照建立完毕以前将再也不响应任何其余命令。save命令并不经常使用,咱们一般只会在没有足够内存去执行bgsave命令的状况下,又或者即便等待持久化操做执行完毕也无所谓的状况下,才会使用这个命令。
  • 若是用户设置了save配置选项,好比save 60 10000,那么从Redis最近一次建立快照以后开始算起,当【60秒以内有10 000次写入】这个条件知足时,Redis就会自动触发bgsave命令。若是用户设置了多个save配置选项,那么当任意一个save的配置选项所设置的条件被知足时,Redis就会触发一次bgsave命令。
  • 当Redis经过shutdown命令接收到关闭服务器的请求时,或者接受到标准term信号时,会执行一个save命令,阻塞全部客户端,再也不执行客户端发送的任何命令,并在save命令执行完毕以后关闭服务器。
  • 当一个Redis服务器链接另外一个Redis服务器,并向对方发送sync命令来开始一次复制操做的时候,若是主服务器目前没有在执行bgsave操做,或者主服务器并不是刚刚执行完bgsave操做,那么主服务器就会执行bgsave命令。

在只使用快照持久化来保存数据时,必定要记住:若是系统真的发生崩溃,用户将丢失最近一次生成快照以后更改的全部数据。所以,快照持久化只适用于那些丢失一部分数据也不会形成问题的应用程序,而不能接受这种数据损失的应用程序则能够考虑使用AOF持久化。接下来将展现几个使用快照持久化的场景,读者能够从中学习到如何经过修改配置来得到资金想要的快照持久化的行为。闭包

我的开发

在我的开发服务器上面,我主要考虑的是尽量地下降快照持久化带来的资源消耗。基于这个缘由以及对本身硬件的信任,我只设置了save 900 1这一条规则。其中save 选项告知Redis,它应该根据这个选项提供的两个值来执行bgsave操做。在这个规则设置下,若是服务器距离上次成功生成快照超过了900秒(15分钟),而且在运行期间执行了至少一次写入操做,那么Redis久自动开始一次新的bgsave操做。函数

若是你打算在生产服务器中使用快照持久化并存储大量数据,那么你的开发服务器最好可以运行在与生产服务器相同或者类似的硬件上面,并在这两个服务器上使用相同的save选项、存储类似的数据集并处理类似的负载量。把开发环境设置得尽可能贴近生产环境,有助于判断快照是否生成的过于频繁或者稀少。过于频繁会浪费资源,过于稀少则带有丢失大量数据的隐患。性能

对日志进行聚合计算

在对日志文件进行聚合计算或者对页面浏览量进行分析的时候,咱们须要惟一考虑的就是:若是Redis由于崩溃而未能成功建立新的快照,那么咱们可以承受丢失多长时间之内产生的新数据。若是丢失一个小时以内产生的数据是能够接受的,那么可使用配置值save 3600 1(3600秒为1小时)。在决定好了持久化配置值以后,另外一个须要解决的问题就是如何恢复由于故障而被终端的日志处理操做。学习

在进行数据恢复时,首先要作的就是弄清楚咱们丢失了哪些数据。为了弄明白这一点,咱们须要在处理日志的同时记录被处理日志的相关信息。大数据

下面代码展现了一个用于处理新日志的函数,该函数有3个参数,他们分别是:一个Redis连接;一个存储日志文件的路径;待处理日志文件中各个行(line)的回调函数。这个函数能够在处理日志文件的同时,记录被处理的日志文件的名字以及偏移量。优化

import os

import redis  # 导入redis包包

#日志处理函数接收的其中以恶搞参数为回调函数
#这个回调函数接收一个Redis链接和一个日志行做为参数,
#并经过调用流水线呢对象的方法来执行Redis命令
def process_log(conn,path,callback):
    #获取文件当前的处理进度
    current_file,offset=conn.mget('progress.file','progress.position')
    pipe=conn.pipeline()

    #经过使用闭包(closur)来减小重复代码
    def update_progress():
        pipe.mset({'progress.file':fname,'progress:position':offset})
        #这个语句负责执行实际的日志更细操做,并将日志文件的名字和目前的处理器进度记录到Redis里面
        pipe.execute()

    #有序的遍历各个日志文件
    for fname in sorted(os.listdir(path)):
        #略过全部已处理的日志文件
        if fname <current_file:
            continue

        inp=open(os.path.join(path,fname),'rb')
        #在接着处理一个由于系统崩溃而未能完成处理的日志文件时,略过已处理的内容
        if fname==current_file:
            inp.seek(int(offset,10))
        else:
            offset=0

        current_file=None

        #枚举函数遍历一个由文件行足哼的序列,并返回任意多个二元组

        #每一个二元祖包含了行号lno和行数据line,其中行号从0开始
        for lno,line in enumerate(inp):
            #处理日志
            callback(pipe,line)
            #更细已处理内容的偏移量
            offset+=int(offset)+len(line)

            #每当处理完1000个日志行或者处理完 整个日志文件的时候,都更新一次文件的处理进度
            if not (lno+1) %1000:
                update_progress()
        update_progress()

        inp.close()

经过将日志的处理进度记录到Redis里面,程序能够在系统崩溃以后,根据进度记录继续执行以前未完成的处理工做。而经过事务流水线,程序保证日志的处理结果和处理进度老是会同时被记录到快照文件里面。

大数据

在Redis存储的数据量只有几个GB的时候,使用快照来保存数据是没有问题的。Redis会建立子进程并将数据保存到硬盘里面,生成快照须要的时间比你读这句话的时间还要短。随着Redis占用的内存愈来愈多,bgsave在建立子进程时耗费的时间愈来愈多。若是Redis的内存占用量达到数十个GB,而且剩余的空闲内存并很少,或者Redis运行在虚拟机上面,那么执行bgsave可能会致使系统长时间地停顿,也可能引起系统大量地使用虚拟内存,从而致使Redis的性能下降至没法使用的程度。

执行bgsave而致使的挺短期有多长取决于Redis所在的系统:对于真实的硬件,vmware虚拟机或者KVM虚拟机来讲,Redis进行每占用一个GB的内存,建立该进程的子进程所需的时间就要增长10~20毫秒,而对于Xen虚拟机来讲,根究配置的不一样,Redis进程每占用一个GB的内存,建立该进程的子进程所需的时间就要增长200~300毫秒。所以,若是咱们的Redis进程占用了20GB的内存,那么在标准硬件上运行bgsave所建立的子进程将致使Reeis停顿200~400毫秒,若是咱们使用的是Xen虚拟机(亚马逊EC2和其余几个云计算供应商都使用这种虚拟机),那么相同的建立子进程操做将致使Redis停顿4~6秒。用户必须考虑本身的应用程序可否接受这种停顿。

为了防止Redeis由于建立子进程而出现停顿,咱们能够考虑关闭自动保存呢,转而经过手动发送bgsave或者save来进行持久化。手动发送bgsave同样会引发停顿,惟一不一样的是用户能够控制停顿出现的时间。另外一方面,虽然save会一直阻塞Redis知道快照生产完毕,可是由于它不须要建立子进程,因此就不会像bgsave同样由于建立子进程而致使Redis停顿;而且由于没有子进程在争抢资源,因此save建立快照的速度比bgsave建立快照的速度要快一些。

根据我的经验,在一台拥有68GB内存的Xen虚拟机上面,对一个占用50GB内存的Redis服务器执行bgsave命令的话,光是建立子进程就须要花费15秒以上,而生成快照则须要花费15~20分钟;但使用save值须要3~5分钟就能够完成快照的生成工做。由于的应用程序只须要天天生成一次快照,因此我写了一个脚本,让它在天天凌晨3点中止全部客户端对Redis的访问,调用save命令并等待该命令执行完毕,以后备份刚刚生成的快照文件,并通知客户端继续执行操做。

若是用户可以妥善地处理快照持久化可能会带来的大量数据丢失,那么快照持久化对用户来讲将使一个不错的选择,但对于不少用于程序来讲,丢失15分钟、1小时甚至更长时间的数据都是不可接受的,在这种状况状况下,咱们可使用AOF持久化来将存储在内存里面的数据尽快的保存到硬盘里面。

上一篇文章: Python--Redis实战:第四章:数据安全与性能保障:第1节:持久化选项
下一篇文章: Python--Redis实战:第四章:数据安全与性能保障:第3节:AOF持久化
相关文章
相关标签/搜索