redis持久化
-
snapshot数据快照(rdb)
这是一种定时将redis内存中的数据写入磁盘文件的一种方案,这样保留这一时刻redis中的数据镜像,用于意外回滚。redis的snapshot的格式是自定义的rdb格式,名称为XXX.rdb,这是一种被压缩的二进制格式。
redis中的数据快照并非仅仅通过crontab这种形式定期执行任务的,可以设置一定时间内数据被写入/更改的次数来触发snapshot操作。配置 save 60 1000, save 1800 1,表示过去的一分钟内执行1000次写入/更改操作或者过去的30分钟内发生一次相关操作就执行snapshot操作,这个叠加的配置表示在1和30分钟两个维度进行检查。同时redis提供SAVE和BGSAVE两个命令接口方便用户手动操作。
redis的snapshot具体实现是:redis的数据存放到一个共享内存中,然后fork出来一个子进程,子进程从共享内存读取全部的缓存数据进行snapshot操作。当上述的某个触发snapshot的条件满足时,记录下当前的timestamp然后开始snapshot操作,将共享内存中的数据写入一个临时的rdb文件,rdb文件写完后,这个时间点redis的snapshot就完成了。在对共享内存进行snapshot时,新来的写入/更改请求会将要修改的数据从共享内存中拷贝一份到临时的内存块中,然后再对数据按照请求内容进行操作。这其中会有一个很大的问题,临时内存块的大小跟在snapshot过程中发生的相关写/更改请求正相关,当redis接入的数据量很大时,要申请的临时内存块会非常大。所以,目前redis的snapshot这种形式的数据持久化会存在一些问题:额外占用大量内存、如果要尽可能降低数据丢失的风险就需要加大备份的频率,但是增大备份的频率又会增大磁盘I/O、临时内存占用,采用这种方案需要用户有所舍弃。 -
append only log增量写日志(aof)
对每一个写入/更改数据的命令都会采用顺序追加的方式记录到一个增量日志文件(aof)中。基于一个时间点的snapshot和从那个时间点的以来的aof,就可以获取到当前的全部数据。增量日志文件的名称为XXX.aof,该文件记录的就是redis命令的协议表达。redis提供了三种aof写磁盘的方式:appendfsync always, appendfsync everysec, appendfsync no;这三种方式分别对应实时flush、每一秒flush、由系统决定何时flush。第一种太消耗性能,第三种无法保证aof的完整性,第二种是两者的折衷,使用的也比较多。
由于aof是记录数据操作命令的,有可能一条数据对应大量的数据操作命令,而且对于大多数命令,只需要记录最新的命令即可,针对这种情况可以对aof日志进行再处理即rewrite。redis提供的rewrite机制类似snapshot,配置文件中auto-aof-rewrite-percentage、auto-aof-rewrite-min-size这两个参数分别对应超过上次文件的百分比、aof文件的大小,只有这两个条件同时满足时才会触发rewrite操作。还提供了BGREWRITEAOF命令用于手动执行rewrite操作。rewrite的实现机制是:redis进程fork出一个子进程,子进程在触发aof日志rewrite操作的时刻负责将共享内存中的全部数据生成相关的数据处理命令(每条或者多条一个命令),然后将这个命令写入一个临时的aof文件中,与此同时父进程也会创建一个aof rewrite buf chunk,这个时间点之后的写入/更改操作会同时写道原来的aof文件和buf chunk中。子进程在完成新的aof文件的生成后,父进程会将buf chunk中的记录追加到新的aof文件中,然后把旧的aof文件删掉,这样就大大减小了aof文件的大小。同时redis还提供了问题:因为aof rewrite相当于将redis的全部数据进行处理生成相关的redis操作命令,这个过程中对cpu、内存、磁盘I/O等资源的消耗都挺大的。
总结
根据上述所谈到的redis的两种持久化方式,都存在一些问题,两种方式同时开启配合使用,可以最大化的提高redis持久化的可靠性。但是在同时开启这两种方式时,aof的flush、rewrite操作都是磁盘I/O密集型的,而rdb的snapshot也是磁盘I/O密集型,所以此时有可能导致底层调用的写磁盘调用fsync阻塞,因此需要修改no-appendfsync-on-rewrite的