关于Redis,也许你想要了解这些内容(二)--持久化

上一次咱们介绍了Redis的数据类型,应用场景,单线程模型以及与Memcached的比较.今天继续介绍它的持久化.mysql

持久化

Redis的数据是存在内存里的,若是不采起特殊措施,一旦redis重启/退出/故障,里面的数据将所有丢失.linux

即使它很快重启也只是个空的redis,这时大量的请求将会直接打到DB去.web

为了不这种状况,Redis提供了RDB和AOF两种持久化方式.redis

RDB


RDB也就是快照,持久化时是把当前内存中的数据集快照写入磁盘(名为dump.rdb的二进制文件),恢复时则把快照文件直接读到内存中.sql

RDB文件的建立数据库

两条命令:安全

  • SAVE阻塞Redis服务器进程,服务器不能接收任何请求,直到RDB文件建立完毕为止。
  • BGSAVE建立出一个 子进程,由子进程来负责建立RDB文件,服务器进程能够继续接收请求。bgsave期间服务器会拒绝任何save命令(防止竞态).

这两条命令既能够由客户端来请求执行,也能够在知足下列条件时自动触发:服务器

  • 用户 设置了save配置选项,好比save 60 10000,那么从最近一次建立快照以后开始算起,当"60秒内有10000次写入"这个条件知足时,redis就会 自动触发bgsave命令.若是设置了多条,知足其中一条就会触发.若是用户没有主动设置,redis也有本身的默认配置
  • 收到 shutdown(关闭服务器)或者标准term信号(linux下终止进程)后,redis会执行save命令,阻塞全部客户端,再也不执行客户端发送的任何命令,并在save命令执行完毕后关闭服务器.
  • 主从复制时,主服务器向从服务器发送一条sync命令,若是此时主服务器没在执行bgsave,也不是刚刚执行完bgsave,就会执行bgsave命令.

save命令的操做比较简单,可是它在执行期间会阻塞线上的业务,因此通常咱们想手动备份的时候都是用的bgsave.要了解bgsave,必须先清楚什么是COW,什么是fork.app

cow也就是copy on write,写时复制.这是一种读写分离的设计思想.编辑器

假设有一块内存空间,咱们要对它进行读写,那么当只有读的需求时,不须要额外操做.若是有写的需求时,另外开一块新的内存空间,把须要写的那页复制到这里,而后就在新的空间进行修改,而写的同时仍旧能够读旧的空间.写完以后把指针指向新的空间,旧的空间抛弃.

这样设计的好处是读写分离,不须要加锁来形成阻塞,写的同时也能够读,提升了读的效率.(联想到CopyOnWriteList没有?)

而fork是linux系统的一个进程机制,应用到了cow.当父进程fork出一个子进程时,二者共享内存里面的代码段和数据段.

RDB的具体执行过程以下:

  • redis调用 forks建立子进程来进行rdb操做.
  • 子进程将数据集写入到一个临时rdb文件中,父进程继续提供读写服务.此时父进程和子进程共享数据段,能够用到 copy-on-write机制.
  • 没必要担忧父进程的读写会改变子进程持久化的数据,由于此时子进程已经把内存的数据 固定下来了.父进程读的时候,依然如旧. 父进程写的时候,子进程会把脏页(即将被写入的那页)复制一份出来让父进程去写.子进程写入硬盘的仍是旧的那页.
  • 当子进程完成对rdb文件的写入时,redis用新的rdb文件替换原来的rdb文件.而fork时产生的那些脏页也会替换原来未修改的那些页.旧页被抛弃.

须要注意的是,在bgsave这个过程当中,服务器执行的写命令并无一块儿同步到rdb文件中,也就是说,RDB文件在bgsave以后并不与当前的数据库状态一致.

RDB文件的载入

rdb文件是在服务器启动的时候自动载入的,redis没有提供任何主动载入rdb文件的命令.

只要redis在启动的时候检测到rdb文件的存在,就会主动载入它.

可是AOF文件的载入优先级高于RDB.只有AOF功能关闭时,redis才会用rdb文件来恢复数据.

tcvxsg.png
tcvxsg.png

AOF


aof(append only file)与rdb文件存储数据集不一样,aof文件存储的是redis全部执行过的命令,相似于mysql的binlog.

AOF的实现

分为三步:

  • 命令追加
  • 文件写入
  • 文件同步

这里要先介绍现代操做系统的文件写入机制:

为了提升效率,在现代操做系统中,当用户试图写入文件时,这些数据会先写入到操做系统的内存缓冲区里,这就是上面说的文件写入.

等到内存缓冲区被填满了,或者超过必定时限后,操做系统才真正把内存缓冲区里的数据写入到硬盘的文件中去.这就是文件同步.

服务器在执行完一个写命令后,就会把这条命令追加到redis的aof缓冲区的末尾.

服务器每次在结束一个事件循环以前,都会根据配置项appendfsync来判断是否要把缓冲区的命令写入到aof文件.

appendfsync有三种选项:

  • always : 将aof缓冲区的数据 写入并同步到aof文件中.
  • everysec(默认) : 写入文件,若是距离上次同步aof文件的时间超过了1秒,那就再同步.
  • no : 只写入文件,不一样步.同步时间由操做系统决定.

使用always,当redis故障时数据丢失最少,但写入硬盘的操做频繁,但效率最慢.

使用no,不只故障时丢失数据最多,并且当缓冲区等待写入硬盘的数据填满时,redis的写操做将被阻塞,因此平均效率与always至关.

使用everysec不只安全,并且实际效率与不进行持久化时相差无几.推荐使用everysec.

AOF的载入

因为aof里包含了redis执行过的全部写命令,当服务器重启时,只需读取并执行aof文件里的全部命令便可还原数据库状态.

可是因为redis的命令只能在客户端上下文中执行,所以这个还原的过程其实是借助于伪客户端来进行的.

tcvzLQ.png
tcvzLQ.png

AOF重写(bgrewriteaof)

RDB文件和AOF文件随着服务器的运行,数据愈来愈多,文件的体积也会膨胀起来.RDB文件没有办法优化,可是AOF能够经过AOF重写来减小体积.

AOF存储的是命令集,有时候对于一条数据反复设置能够产生多条命令(过程).因为还原数据库只须要最终的数据(结果),所以能够想办法把这多条命令合并到一条命令上.好比aof记录了set a 1, set a 2,set a 3三条命令,那么咱们最终只须要set a 3这条等效命令.

实际上,AOF重写并不依赖AOF文件,而是从数据库一个个读取key如今的值,而后把一条条set命令写入到新aof文件去.写完以后用新aof文件替代旧aof文件,那么重写就完成了.

AOF后台重写

aof重写须要读取所有的key,若是只经过一个线程去操做的话,无疑会形成严重阻塞,在此期间没法继续处理客户端的请求(有没有想到keys命令?)

因此Redis将重写放入到子进程去进行,让父进程继续处理客户端的读写请求.(和bgsave类似).

一样地,在重写期间,若是父进程继续修改数据库的数据,那么重写后的aof文件与当前数据库的状态并不一致.

为了解决这种数据不一致的问题,redis设置了一个aof重写缓冲区(与aof缓冲区区分开),在fork子进程以后使用.因此在重写期间,服务器进程会执行如下三个工做:

  • 执行客户端发来的命令.
  • 将执行后的写命令追加到aof缓冲区末尾.
  • 将执行后的写命令追加到aof重写缓冲区末尾.(新增)
tcvvQS.png
tcvvQS.png

当子进程重写完成后,会把aof重写缓冲区里的命令(未优化过)写入到新的aof文件里,这样就能够保证新aof文件与数据库状态的一致性了.

因此要注意的是,即使aof刚刚重写完毕,它也是还能够再压缩的.

顺便一提,主从复制时也会用到这个机制.

RDB与AOF对比

  • RDB存储数据集,每次持久化是 全量存储.AOF存储命令集,每次持久化只存储 增量.
  • RDB持久化时间更长,可能丢失的数据也更多.
  • RDB文件通常会小于AOF文件(未重写),用于还原数据库状态时的速度也会快于AOF
  • 可是因为aof文件的更新频率更高,丢失数据的数量也更小,所以服务器启动时默认载入aof文件.
  • 因为RDB文件紧凑性,便于复制数据到一个远端数据中心,很是适用于灾难恢复,适合 冷备.
  • AOF更新频率快,可能丢失的数据少,适合 热备.

为了防止服务器忽然宕机的状况,Redis在运行期间必定要开启aof进行备份.

可是若是Redis是正常退出,它会自动生成RDB文件,这个RDB文件保存的是最新的数据集.那么重启时,能够把aof文件删掉,载入这个RDB文件以快速恢复.

RDB和AOF混合搭配模式

在对redis进行恢复的时候,若是咱们采用了RDB的方式,由于bgsave的策略,可能会致使咱们丢失大量的数据。若是咱们采用了AOF的模式,经过AOF操做日志重放恢复,重放AOF日志比RDB要长久不少。

redis4.0以后,为了解决这个问题,引入了新的持久化模式,混合持久化,将rdb的文件和局部增量的AOF文件相结合,rdb可使用相隔较长的时间保存策略,aof不须要是全量日志,只须要保存前一次rdb存储开始到这段时间增量aof日志便可,通常来讲,这个日志量是很是小的。

相关文章
相关标签/搜索