本文内容为作者读老钱的《Redis深度历险》的笔记摘抄,这是一本非常值得读的Redis书籍。
前言
Redis的持久化是使Redis的热度超过Memcached的关键因素之一。因为其我们享受了内存带来的高性能I/O,也让我们能够保证数据不会因为Redis的故障而丢失。
持久化机制
Redis的持久化机制有两种:RDB(快照)和AOF日志。RDB是一次性全量备份,而AOF日志是连续的增量备份。前者是内存数据的二进制格式,而后者是记录内存数据修改的记录文本。所以AOF日志的备份文件会比RDB大得多,于是需要定期的为AOF进行重写,进行瘦身。
RDB原理
先说说快照的原理,我们都知道Redis是单线程程序,这个内存要同时负责多个客户端Socket的并发I/O和内存数据结构逻辑的I/O。如果我们盲目的对Redis进行内存快找就会占用其线程阻塞线上的业务请求。
那么Redis是如何解决这个问题的呢?
COW(Copy On Write)
这是OS的多进程机制来实现持久化,这个机制很有意思。
首先Redis在持久化的时候会fork出一个子进程,然后将redis快照持久化的任务完全的交给了子进程来实现,父进程继续处理客户端的请求。子进程刚产生的时候会和父进程共享内存中的数据空间,就像是一个连体婴儿。这是Linux操作系统的机制,为了节约内存资源,所以尽可能的让他们共享起来。在进程分离的一瞬间,内存的增长不会有明显的变化。
子进程做数据持久化,遍历对数据结构进行读取然后序列化写到磁盘中。那么如何保证父进程的业务请求不影响到子进程呢? 这就用到了COW机制,分离后的父进程每当要对数据段进行修改的时候,父子共享的数据段就会被分离出来,然后父进程会去修改这个复制的数据段,子进程依然读取原本的数据段,这样就能够做到对子进程不产生影响。
AOF原理
AOF日志存储的是Redis的顺序指令序列,只会记录对内存进行修改的记录。所以,如果AOF日志存储了一个Redis实例创建以来所有的指令序列,那么对一个空的Redis进行重放就能恢复前一个Redis实例的内存数据结构的状态。
开启了AOF之后,Redis每收到一个指令,就会进行参数校验,没有问题就会先写入AOF日志中(也就是磁盘中),后再执行指令。这样即使突然宕机,只要对AOF日志中的指令重放一下就能够恢复宕机前的状态了。
AOF瘦身
Redis在长期运行的过程中,本身的内存占用可能会上下变化,但是AOF日志文件所占用的磁盘空间却是一直在不断增长的。同时如果一个长期运行的Redis实例宕机,因为AOF日志文件的庞大,重放的时间也会无限增长。所以需要对AOF日志进行瘦身。
Redis提供了bgrewriteaof指令来对AOF日志进行瘦身。而其中的原理很简单也是通过fork开辟一个子进程,然后子进程对内存的中的数据进行遍历后转换成一系列的Redis的操作指令序列化到一个新的AOF日志文件中。序列化完毕后,在通过之前的AOF文件重放一下,子进程从创建到序列化操作期间,父进程发生的增量AOF,追加到新的AOF文件中,立即替换旧的AOF日志文件。到此瘦身工作就完成了。
fsync
AOF日志是以文件的形式存在的,当服务器程序对AOF日志文件进行写操作的时候,不是直接写到磁盘中,而是写到了内核的为文件描述符分配的一个内存缓存中的,然后内核会一步的将数据刷回磁盘。
那么在这个阶段中,机器突然宕机了,AOF的内容可能还没有完全来得及刷到磁盘中,就会出现日志丢失的情况,那么怎么办呢?
Linux的glibc提供了fsync(int fd)函数来指定文件的内容强制从内核缓存中刷到磁盘上。只要Redis能够在实时调用这个函数就能够造成数据不掉时。但是fsync是一个磁盘I/O的操作,它很慢!如果一条指令就要刷新一次,Redis就要告别高性能的地位了。
所以在生产环境中通常设定都是1s左右执行一次fsync的操作。
混合持久化
RDB不能够做到实时保存Redis的数据,而是每隔一段时间的一次全量备份;而AOF的增量备份将会影响Redis的性能。所以在Redis4.0之后推出了混合持久化的选项。将RDB文件的内容和AOF放在一起。
这样AOF的内容不再是从Redis持久化开始到结束这段时间的指令序列,而是从上一次RDB到持久化结束的指令序列。通常这部分AOF日志很小。
于是Redis重启之后就可以先加载RDB的内容,然后重放AOF日志,重启的效率得到了大幅度的提升。
|