冰雨战士

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

前言

项目上经常用到redis进行持久化存储,却不知道redis持久化的原理,这里转载一篇博客,权当记读书笔记了,原文地址:http://blog.nosqlfan.com/html/3813.html

写操作的流程

首先我们来看一下,数据库在进行写操作时到底做了哪些事,主要有下面五个过程:
  1. 客户端向服务器端发送写操作(数据在客户端的内存中)
  2. 数据库服务端接收到写请求的数据(数据在服务器端的内存中)
  3. 服务器端调用write(2)这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)
  4. 操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)
  5. 磁盘控制器将数据写到磁盘的物理介质中(数据真正落在磁盘上)

故障分析

写操作大致上有上面5个流程,下面我们结合上面的5个流程看一下各种级别的故障。
  • 当数据库系统故障时,这时候系统内核还是OK的,那么此时只要我们执行完第3步,那么数据就是安全的,因为后续操作系统会来完成后面几步,保证数据最终会落在磁盘上
  • 当系统断电,这时候上面5项提到的所有缓存都会失效,并且数据库和操作系统都会停止工作。所以只有当数据在完成第5步后,机器断电才能保证数据不丢失,在上述四步中的数据都会丢失
通过上面5步的了解,可能我们会希望搞清下面的一些问题:
  1. 数据库多长时间调用一个write(2),将数据写到内核缓冲区
  2. 内核多长时间会将系统缓冲区中的数据写到磁盘控制器
  3. 磁盘控制器又在什么时候把缓存中的数据写道物理介质上
对于第一个问题,通常数据库层面会进行全面控制。而对第二个问题,操作系统有其默认的策略,但是我们也可以通过POSIX API提供的fsync系列命令强制操作系统将数据从内核区写到磁盘控制器上。对于第三个问题,好像数据库已经无法触及,但实际上,大多数情况下磁盘缓存 是被设置关闭的,或者是只开启为读缓存,也就是写操作不会进行缓存,直接写到磁盘。建议的做法是仅仅当你的磁盘有备用电池时才开启写缓存。


数据损坏

所谓数据损坏,就是数据无法恢复,上面我们讲的都是如何保证数据是确实写到磁盘上去,但是写到磁盘上可能并不意味着数据不会损坏。比如我们可能 一次写请求会进行两次不同的写操作,当意外发生时,可能会导致一次写操作安全完成,但是另一次还没有进行。如果数据库的数据文件结构组织不合理,可能就会 导致数据完全不能恢复的状况出现。
这里通常也有三种策略来组织数据,以防止数据文件损坏到无法恢复的情况:
  1. 最粗糙的处理,就是不通过数据的组织形式保证数据的可恢复性。而是通过配置数据同步备份的方式,在数据文件损坏后通过数据备份来进行恢复。
  2. 另一种是在上面基础上添加一个操作日志,每次操作时记一下操作的行为,这样我们可以通过操作日志来进行数据恢复。因为操作日志是顺序追加的方式写的,所以不会出现操作日志也无法恢复的情况
  3. 更保险的做法是数据库不进行老数据的修改,只是以追加方式去完成写操作,这样数据本身就是一份日志,这样就永远不会出现数据无法恢复的情况了

RDB快照

下面我们说一下Redis的第一个持久化策略,RDB快照。Redis支持将当前数据的快照存成一个数据文件的持久化机制。而一个持续写入的数 据库如何生成快照呢?Redis借助了fork命令的copy on write机制。在生成快照时,将当前进程fork出一个子进程,然后在子进程中循环所有的数据,将数据写成为RDB文件

我们可以通过Redis的save指令来配置RDB快照生成的时机,比如你可以配置当10分钟以内有100次写入就生成快照,也可以配置当1小 时内有1000次写入就生成快照,可以多个规则一起实施。这些规则的定义就在Redis的配置文件中,你可以通过Redis的CONFIG SET命令在Redis运行时设置规则,不需要重启Redis

Redis的RDB文件不会坏掉,因为其写操作是在一个新进程中进行的,当生成一个新的RDB文件时,Redis生成的子进程会先将数据写到一 个临时文件中,然后通过原子性rename系统调用将临时文件重命名为RDB文件,这样任何时间出现故障,Redis的RDB文件都总是可用的。

同时,Redis的RDB文件也是Redis主从同步内部实现的一环。

但是,我们可以很明显的看到,RDB有他的不足,就是一旦数据库出现问题,那么我们的RDB文件中保存的数据并不是全新的,从上次RDB文件生 成到Redis停机这段时间的数据全部丢掉了。在某些业务下,这是可以忍受的,我们也推荐这些业务使用RDB的方式进行持久化,因为开启RDB的代价并不 高。但是对于另外一些对数据安全性要求极高的应用,无法忍受数据丢人的应用,RDB就无能为力了,所以Redis引入了另一个重要的持久化机制:AOF日 志。


AOF日志

aof日志的全称是append only file,从名字上我们就能看出来,它是一个追加写入的日志文件。与一般数据库的binlog不同的是,AOF文件是可识别的纯文本,它的内容就是一个个 的Redis标准命令。比如我们进行如下实验,在启动命令参数中设置开启aof功能:

  1. sudo sed  -i "s/appendonly no/appendonly yes/g" /etc/redis/redis.conf && sudo /etc/init.d/redis-server restart  

然后我们执行如下命令:




这时我们查看AOF日志文件,就会得到如下内容:



可以看到,写操作都生成一条相应的命令作为日志。其中值得注意的是最后一个del命令,它并没有被记录在AOF日志中,这是因为Redis判断 出这个命令不会对当前数据集做出修改。所以不需要记录这个无用的写命令。另外AOF日志也不是完全按客户端的请求来生成日志的,比如命令 INCRBYFLOAT日志时就被记成一条SET记录,因为浮点数操作可能在不同的系统上会不同,所以为了避免同一份日志在不同的系统上生成不同的数据 集,所以这里只将操作后的结果通过SET来记录


AOF重写

你可能会想,每一条命令都生成一条日志,那么AOF文件是不是会很大?答案是肯定的,AOF文件会越来越大,所以Redis又提供了一个功能, 叫做AOF rewrite。其功能就是重新生成一份AOF文件,新的AOF文件中一条的操作只会有一次,而不像一份老文件那样,可能记录了对同一个值的多次操作。其 生成过程和RDB类似,也是fork一个进程,直接遍历数据,写入新的AOF临时文件。在写入新文件的过程中,所有的写操作日志还是会写道原来老的AOF 文件中,同时还会记录在内存缓冲区中。当操作完成后,会将所有缓冲区中的日志一次性写入到临时文件中。然后调用原子性rename命名用新的AOF文件取 代老的AOF文件

从上面的流程我们能够看到,RDB和AOF操作都是顺序IO操作,性能都很高。而同时在通过RDB文件或者AOF日志进行数据库恢复的时候,也是顺序的读取数据加载到内存中,所以也不会造成磁盘的随机读。


数据导入

着些持久化的数据有什么用,当然是用于重启后的数据恢复。Redis是一个内存数据库,无论是RDB还是AOF,都只是保证数据恢复的措施。所 以Redis在利用RDB和AOF进行恢复的时候,都会读取RDB或AOF文件,重新加载到内存中。相对于mysql等数据库的启动时间来说,会长很多, 因为Mysql本来是不需要将数据加载到内存中的。

但是相对来说,Mysql启动后提供服务时,其被访问的热数据也会慢慢加载到内存中,通常我们称之为预热,而在预热完成前,其性能都不会太高。而Redis的好处就是一次性将数据加载到内存中,一次性预热。这样只要Redis启动完成,那么其提供服务的速度都是非常快的。

而在利用RDB和利用AOF启动上,其启动时间有一些差别。RDB的启动时间会更短,原因是两个,一个RDB文件中每一条数据只有一条记录,不 会像AOF日志那样可能会有一条数据的多次操作记录。所以每条数据只需要写一次就行了。另一个原因是RDB文件的存储格式和Redis数据在内存中的编码 格式是一致的,不需要再进行数据编码工作。在CPU消耗上要远小于AOF日志的加载。

posted on 2013-03-08 10:27  冰雨战士  阅读(249)  评论(0编辑  收藏  举报