redis 的两种持久化策略及原理

Redis是一种高级key-value数据库。它跟memcached相似,不过数据能够持久化,并且支持的数据类型很丰富。有字符串,链表,集 合和有序集合。支持在服务器端计算集合的并,交和补集(difference)等,还支持多种排序功能。因此Redis也能够被当作是一个数据结构服务 器。redis

Redis的全部数据都是保存在内存中,而后不按期的经过异步方式保存到磁盘上(这称为“半持久化模式”);也能够把每一次数据变化都写入到一个append only file(aof)里面(这称为“全持久化模式”)。数据库

 

第一种方法filesnapshotting:默认redis是会以快照的形式将数据持久化到磁盘的(一个二进 制文件,dump.rdb,这个文件名字能够指定),在配置文件中的格式是:save N M表示在N秒以内,redis至少发生M次修改则redis抓快照到磁盘。固然咱们也能够手动执行save或者bgsave(异步)作快照。缓存

工做原理简单介绍一下:当redis须要作持久化时,redis会fork一个子进程;子进程将数据写到磁盘上一个临时RDB文件中;当子进程完成写临时文件后,将原来的RDB替换掉,这样的好处就是能够copy-on-write安全

还有一种持久化方法是Append-only:filesnapshotting方法在redis异常死掉时, 最近的数据会丢失(丢失数据的多少视你save策略的配置),因此这是它最大的缺点,当业务量很大时,丢失的数据是不少的。Append-only方法可 以作到所有数据不丢失,但redis的性能就要差些。AOF就能够作到全程持久化,只须要在配置文件中开启(默认是no),appendonly yes开启AOF以后,redis每执行一个修改数据的命令,都会把它添加到aof文件中,当redis重启时,将会读取AOF文件进行“重放”以恢复到 redis关闭前的最后时刻。服务器

LOG Rewriting随着修改数据的执行AOF文件会愈来愈大,其中不少内容记录某一个key的变化状况。所以redis有了一种比较有意思的特性:在后台重建AOF文件,而不会影响client端操做。在任什么时候候执行BGREWRITEAOF命令,都会把当前内存中最短序列的命令写到磁盘,这些命令能够彻底构建当前的数据状况,而不会存在多余的变化状况(好比状态变化,计数器变化等),缩小的AOF文件的大小。因此当使用AOF时,redis推荐同时使用BGREWRITEAOF数据结构

AOF文件刷新的方式,有三种,参考配置参数appendfsync :appendfsync always每提交一个修改命令都调用fsync刷新到AOF文件,很是很是慢,但也很是安全;appendfsync everysec每秒钟都调用fsync刷新到AOF文件,很快,但可能会丢失一秒之内的数据;appendfsync no依靠OS进行刷新,redis不主动刷新AOF,这样最快,但安全性就差。默认并推荐每秒刷新,这样在速度和安全上都作到了兼顾。app

可能因为系统缘由致使了AOF损坏,redis没法再加载这个AOF,能够按照下面步骤来修复:首先作一个AOF文件的备份,复制到其余地方;修复原始AOF文件,执行:$ redis-check-aof –fix ;能够经过diff –u命令来查看修复先后文件不一致的地方;重启redis服务。运维

LOG Rewrite的工做原理:一样用到了copy-on-write:首先redis会fork一个子进程;子进程将最新的AOF写入一个临时文件;父进程 增量的把内存中的最新执行的修改写入(这时仍写入旧的AOF,rewrite若是失败也是安全的);当子进程完成rewrite临时文件后,父进程会收到 一个信号,并把以前内存中增量的修改写入临时文件末尾;这时redis将旧AOF文件重命名,临时文件重命名,开始向新的AOF中写入。异步

最后,为以防万一(机器坏掉或磁盘坏掉),记得按期把使用 filesnapshotting 或 Append-only 生成的*rdb *.aof文件备份到远程机器上。我是用crontab每半小时SCP一次。我没有使用redis的主从功能 ,由于半小时备份一次应该是能够了,并且我以为有若是作主从有点浪费机器。这个最终仍是看应用来定了。memcached

 

 

========================

 

 

数据持久化通俗讲就是把数据保存到磁盘上,保证不会由于断电等因素丢失数据。

redis 须要常常将内存中的数据同步到磁盘来保证持久化。redis支持两种持久化方式,一种是 Snapshotting(快照)也是默认方式,另外一种是Append-only file(缩写aof)的方式。先介绍下这两种dump方式再讲讲本身遇到的一些现象和想法,前面的内容是从网上整理出来的。

Snapshotting
快照是默认的持久化方式。这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。能够经过配置设置自动作快照持久 化的方式。咱们能够配置redis在n秒内若是超过m个key被修改就自动作快照,下面是默认的快照保存配置

save 900 1  #900秒内若是超过1个key被修改,则发起快照保存
save 300 10 #300秒内容如超过10个key被修改,则发起快照保存
save 60 10000

下面介绍详细的快照保存过程

1.redis调用fork,如今有了子进程和父进程。

2. 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。因为os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面建立副本,而不是写共享的页面。因此子进程的地址空间内的数 据是fork时刻整个数据库的一个快照。

3.当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,而后子进程退出。

client 也可使用save或者bgsave命令通知redis作一次快照持久化。save操做是在主线程中保存快照的,因为redis是用一个主线程来处理全部 client的请求,这种方式会阻塞全部client请求。因此不推荐使用。另外一点须要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不 是增量的只同步脏数据。若是数据量大的话,并且写操做比较多,必然会引发大量的磁盘io操做,可能会严重影响性能。

另外因为快照方式是在必定间隔时间作一次的,因此若是redis意外down掉的话,就会丢失最后一次快照后的全部修改。若是应用要求不能丢失任何修改的话,能够采用aof持久化方式。下面介绍

Append-only file

aof 比快照方式有更好的持久化性,是因为在使用aof持久化方式时,redis会将每个收到的写命令都经过write函数追加到文件中(默认是 appendonly.aof)。当redis重启时会经过从新执行文件中保存的写命令来在内存中重建整个数据库的内容。固然因为os会在内核中缓存 write作的修改,因此可能不是当即写到磁盘上。这样aof方式的持久化也仍是有可能会丢失部分修改。不过咱们能够经过配置文件告诉redis咱们想要 经过fsync函数强制os写入到磁盘的时机。有三种方式以下(默认是:每秒fsync一次)

appendonly yes              //启用aof持久化方式
# appendfsync always      //每次收到写命令就当即强制写入磁盘,最慢的,可是保证彻底的持久化,不推荐使用
appendfsync everysec     //每秒钟强制写入磁盘一次,在性能和持久化方面作了很好的折中,推荐
# appendfsync no    //彻底依赖os,性能最好,持久化没保证

aof 的方式也同时带来了另外一个问题。持久化文件会变的愈来愈大。例如咱们调用incr test命令100次,文件中必须保存所有的100条命令,其实有99条都是多余的。由于要恢复数据库的状态其实文件中保存一条set test 100就够了。为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis将使用与快照相似的方式将内存中的数据 以命令的方式保存到临时文件中,最后替换原来的文件。具体过程以下

1. redis调用fork ,如今有父子两个进程
2. 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
3.父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证若是子进程重写失败的话并不会出问题。
4.当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。而后父进程把缓存的写命令也写入到临时文件。
5.如今父进程可使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。

须要注意到是重写aof文件的操做,并无读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点相似。

运维上的想法

其实快照和aof同样,都使用了Copy-on-write技术。屡次试验发现每次作数据dump的时候,内存都会扩大一倍(关于这个问题能够参考我去年写的redis的内存陷阱,不少人用redis作为缓存,数据量小,dump耗时很是短暂,因此不太容易发现),这个时候会有三种状况:

一:物理内存足以知足,这个时候dump很是快,性能最好

二:物理内存+虚拟内存能够知足,这个时候dump速度会比较慢,磁盘swap繁忙,服务性能也会降低。所幸的是通过一段比较长的时候数据dump完成了,而后内存恢复正常。这个状况系统稳定性差。

三: 物理内存+虚拟内存不能知足,这个时候dump一直死着,时间久了机器挂掉。这个状况就是灾难!

若是数据要作持久化又想保证稳定性,建议留空一半的物理内存。若是以为没法接受仍是有办法,下面讲:

快照和aof虽然都使用Copy-on-write,但有个不一样点,快照你没法预测redis何时作dump,aof能够经过bgrewriteaof命令控制dump的时机。

根据这点我能够在一个服务器上开启多个redis节点(利用多CPU),使用aof的持久化方式。

例 如在24G内存的服务器上开启3个节点,天天用bgrewriteaof按期从新整理数据,每一个节点dump的时间都不同,这 样理论上每一个节点能够消耗6G内存,一共使用18G内存,另外6G内存在单个节点dump时用到,内存一下多利用了6G! 固然节点开的越多内存的利用率也越高。若是带宽不是问题,节点数建议 = CPU数。

个人应用里为了保证高性能,数据没有作dump,也没有用aof。由于不作dump发生的故障远远低于作dump的时候,即便数据丢失了,自动修复脚本能够立刻数据恢复。毕竟对海量数据redis只能作数据分片,那么落到每一个节点上的数据量也不会不少。

redis的虚拟内存建议也不要用,用redis原本就是为了达到变态的性能,虚拟内存、aof看起来都有些鸡肋。

如今还离不开redis,由于它的mget是如今全部db里性能最好的,之前也考虑过用tokyocabinet hash方式作mget,性能不给力。直接用redis,基本上单个redis节点mget能够达到10W/s

纠错

以前说过redis作数据dump的时候内容会扩大一倍,后来我又作了些测试,发现有些地方说的不对。

top 命令并非反映真实的内存占用状况,在top里尽管fork出来的子进程占了和父进程同样的内存,可是当作dump的时候没有写操做,实际使 用的是同一分内存的数据。当有写操做的时候内存才会真实的扩大(具体是否是真实的扩大一倍不肯定,可能数据是按照页分片的),这才是真正的Copy- on-write。

基于这点在作数据持久化会更加灵活。

相关文章
相关标签/搜索