Redis的持久化机制有两种,第一种是快照,第二种是AOF日志.
快照
快照是一次全量备份,是内存数据的二进制序列化形式,在存储上非常紧凑.
快照原理
Redis使用操作系统的多进程COW(Copy On Write)机制来实现快照持久化.
fork(多进程)
Redis在持久化时会调用glibc的函数fork产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求. 子进程刚刚产生时,他和父进程共享内存里面的代码段和数据段. 用Python语言描述进程分离的逻辑如下.fork函数会在父子进程同时返回,在父进程里返回子进程的pid,在子进程里返回0. 如果操作系统内存资源不足,pid就会是负数,表示fork失败.子进程做数据持久化,他不会修改现有的内存数据结构,他只是对数据结构进行遍历读取,然后持久化到磁盘中.但是父进程不一样,他必须持续服务客户端请求,然后对内存数据结构进行不间断的修改. 这个时候就会使用操作系统的COW机制来进行数据段页面的分离.数据段是由很多操作系统的页面组合而成,当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份出来,然后对这个复制的页面进行修改.此时子进程相应的页面是没有变化的. 随着父进程修改操作的持续进行,越来越多的共享页面被分离出来,内存就会持续增长.但是也不会超过原来数据内存的2倍大小.另外一个redis实例里冷数据占的比例往往比较高,所以很少会出现所有的页面都被分离的情况,被分离的往往只有其中一部分页面.每个页面的大小只有4K. 接下来子进程就可以遍历数据进行序列化写磁盘了
AOF日志
AOF日志是连续的增量备份,存储数据修改的指令记录文本(类似MySQL的redo log),AOF日志在长期的运行过程中会变得无比庞大,数据库重启时需要加载AOF日志进行指令重放,这个时间就会变得无比漫长.所以需要定期进行AOF重写,给AOF日志瘦身.
AOF原理
AOF日志存储的是Redis服务器的顺序指令序列,AOF日志只记录对内存进行修改的指令. Redis会在收到客户端修改指令后,先进行参数校验,如果没问题,就立即将该指令文本存储到AOF日志中,也就是先存磁盘,然后在执行指令.这样即使遇到突发宕机,已经存放到AOF日志中的指令进行重放就可以恢复到宕机前的状态了.
AOF重写
Redis提供了bgrewriteaof指令用于对AOF日志进行瘦身.其原理就是开辟一个子进程对内存进行遍历转换成一系列Redis的操作指令,序列化到一个新的AOF日志文件中.序列化完后再将操作期间发生的增量AOF日志追见到这个新的AOF日志文件中,追加完毕后就替代旧的AOF日志文件.
fsync
AOF日志是以文件的形式存在的,当程序对AOF日志文件进行写操作时,实际上是将内容写到了内核为文件描述符分配的一个内存缓存中,然后内核会异步将脏数据刷回到磁盘
这就意味着如果机器突然宕机,AOF日志内容可能还没有来得及完全刷到磁盘中,这个时候就会出现日志丢失. 关于这个问题linux的glibc提供了fsync(int fb)函数可以将指定文件的内容强制从内核缓存刷到磁盘.只要Redis进程实时调用fsync函数就可以保证aof日志不丢失.但是fsync是一个磁盘IO操作,速度很慢! 如果Redis执行一条指令就药fsync一次,那么Redis的性能就会出现问题. 所以在生产环境的服务器中,Redis通常每隔一秒左右执行一次fsync操作,周期时间是可以配置的.