冷湘宇 Coldxiangyu's blog

redis深入研究【持久化】

2016-04-06
   

之前一直有一个疑问,redis作为一个支持持久化的内存数据库,肯定是要将数据存入磁盘的,但redis究竟是如何将内存中的数据进行持久化存储的呢? 在网上查了一下redis持久化,redis提供了四种持久化的方案。

  • 快照模式
  • AOF(append-only file)模式
  • 虚拟内存方式(已弃用)
  • diskstore方式

下面重点关注一下前两个,而在实战场景中用的最多的就是快照模式了,所以重点了解一下。

1 RDB(SNAPSHOTTING)快照模式

快照模式是redis的默认持久化方式,这种方式会将内存中的数据以快照的方式写入到二进制文件中,默认文件名为dump.rdb。redis.conf中也有对应的配置:

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

RDB文件存储结构如下: 此处输入图片的描述
根据这个存储结构,我们对dump.rdb文件进行研究,因为之前我们所有的操作都没有进行save,因此redis内存中的数据并不会写入磁盘文件,我们打开redis目录也并没有发现这个文件。
接下来我们save一下,将数据快照到磁盘:

127.0.0.1:6379> save
OK
127.0.0.1:6379>

之后,dump.rdb二进制文件生成。
用UE打开文件,如下: image_1bcufptq67upbfcj1b128r19nr9.png-25.8kB 1.redis参数:可以看到在16进制的前5个字节中,是“REDIS”五个大字母,这个的作用显而易见,肯定就是判断当前的文件是否为RDB文件
2.db_version:在Redis字符之后,我们看到了占用4个字节的0006,这个就是RDB文件结构图中的 db_version。
3.database:也就是存储的核心数据,从图中我们大概可以看到非中文的key,value ,比如 < code , test>
4.EOF:在最后一行的FF,表示database结束
5.checksum:占用8个字节,校验和,用于校验文件是否被修改或者损坏,也就是末位的:D8 2D 80 ED EF B4 4A 33

其中,在database数据中,又分为
1.selectDB:在第一行的第9个字节,也就是FE,标记着数据库的开始
2.db_number:在FE之后,表示对应存储的数据库,在redis-cli中编写select db_number即可选择要操作的数据库,对应生成的rdb文件也会体现出来。为了方便观察,我把数据标记一下: image_1bcuh0h8clbg10rb5v982625813.png-24.8kB

从图中我们可以看出,整个RDB文件一共有4个数据库存储了数据,分别是00、02、04、05号数据库,那么实际上是不是这样的呢,我们通过redis可视化工具直观的看一下数据实际的存储情况: image_1bcuhaberh3ndj2f7iqnic602n.png-28.4kB

image_1bcuharkqmhua7h15j1tav14hg34.png-18.8kB

image_1bcuhbc711cn1jmv9e9qq81kqk3h.png-20.7kB

image_1bcuhbt6g1c001qr1847964r9b3u.png-22.3kB

其实,database这层结构再往下还是可以再分的,我们以04号数据库数据为例: image_1bcui6eum1613jqkvj11qc01fhh4b.png-6.8kB

其中,FE 04 代表选择04号数据库,再往后,00代表数据编码(具体编码需要看源码,见下图),09代表key值的长度,也就是对应的key值“jedisPool”的length。 此处输入图片的描述

再往下就有点疑问了,按照正常的情况,value值也应该遵循key值存储的规则,即[len][value]这种形式,但是实际我在文件中看到的是以0F,即“.”分隔key和value的,我怀疑是redis不同版本的问题,也可能是windows版本和linux版本RDB文件存储存在差异,具体原因我也没有查到,只能等以后有Linux服务器资源了再去验证了。

2 AOF模式

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文件,这点和快照有点类似。

下面链接是对比两种持久化方式,写的非常好,反正我总结不了这么好,所以直接外链了: http://blog.csdn.net/u012129558/article/details/51507588


Similar Posts

Comments