Redis原理 — 持久化

Redis原理 — 持久化

edis 的存储机制有两种,快照(RDB)和AOF。快照存储某一个时间节点的全量备份,AOF日志是连续的增量备份。快照是内存数据的二进制序列化形式,存储十分紧凑,AOF日志是记录数据修改的指令文本,AOF在长期运行中会变得十分庞大,所以需要定期对 AOF 日志进行重写,给系统瘦身。

快照原理

Redis 是单线程程序,之所以能够高效处理多个客户端的并发读写请求得益于其基于操作系统的「多路复用API」机制,而内存快照的备份需要频繁进行文件 IO 操作,文件 IO 操作不支持多路复用IO,这就意味着快照的备份过程中客户端请求的处理将会阻塞,而在实际使用过程请求并没有受到影响,因为 Redis 的快照备份是使用操作系统的多进程 COW(Copy On Write)机制来实现的。

Redis 在持久化时,会创建一个子线程来专门做持久化任务,父进程继续处理客户端请求。子线程刚开始创建时,共享父进程的数据,这是基于 linux 操作系统机制,为了节约资源。

子线程持久化某个 key 时,父进程修改了这个 key 怎么办?

Redis 使用操作系统的 COW 机制进行数据段页面的分离,数据段由很多页面组成,父进程对某个页面做修改操作时,会将该页面复制一份出来,在新复制的页面上做修改,而子线程读取的仍然是旧页面数据(进程创建时的瞬时数据)

父进程的修改操作进行中,越来越多的页面被复制出来,但是不会超过内存数据的 2 倍,由于 redis 中的冷数据占比往往比较高,所以很少出现所有页面被复制的情况。

子线程由于数据段页面没有发生变化,它能看到的内存数据在线程创建时就固定不变了,所以也叫做「快照」,接下来子线程可以遍历数据系列化进磁盘了。

AOF原理

AOF日志存储的是 Redis 服务器的顺序指令序列,AOF日志只记录对数据的修改指令。每次重启服务器,相当于对一个空实例执行所有指令,实现「重放」来恢复 Redis 当前实例的内存数据结构的状态。

Redis 在长期运行中,AOF日志会越来越大,如果实例宕机重启,恢复数据将会十分缓慢,导致 redis 长时间无法对外提供服务,所以需要定期对 AOF 日志进行重写。

AOF重写

Redis 提供 bgrewriteaof 指令对 AOF 日志瘦身,它将开辟一个子线程,遍历内存的所有数据,转换成一系列指令,序列化到一个新的 AOF 日志。然后再将写入期间发生的修改指令追加到新的 AOF 日志,最后替换旧的 AOF 日志文件完成重写。

fsync 强制将指令从缓存刷入磁盘

AOF日志是以文件形式存在的,当对AOF日志进行写操作时,操作系统内核会将数据先写入到缓存中,再异步地慢慢刷回磁盘。如果数据写入到缓存还没来得及刷回磁盘,系统宕机了,那么会使得 Redis 丢失其间的数据。

Linux 系统提供了一个 fsync(int fd) 函数用于强制将内核缓存中的数据写入磁盘,Redis 进程只要实时的调用 fsync 函数就能保证 AOF 日志不丢失,但是 fsync 是一个很耗 IO 的操作,不可能每执行一条指令就 fsync 一次,这会使得性能大打折扣。

通常情况下,Redis 每隔 1s fsync 一次,在保证高性能的同时,尽可能的不丢失数据。用户也可以根据业务量自行配置 fsync 频率。

运维

在实际使用中需要知道:1. 快照是通过子线程进行的,是一个比较耗时的操作,它会遍历整个内存,大块写磁盘会加重系统负载;2. AOF 的 fsync 是一个很耗时的 IO 操作,会降低 redis 的性能,同时也会增加系统IO负担,需要合理配置参数。

通常情况下,配置主从节点,主节点负责处理客户端请求,从节点当作备份节点专门负责数据持久化,同时也需要注意保持主从节点之间的网络畅通,以避免从节点连接不上主节点造成数据丢失。

Redis4.0 混合持久化

通常情况下,重启 Redis 很少使用快照恢复数据,因为很有可能丢失大量数据。通常使用 AOF 日志重放,但是重放相对于快照来说会慢很多,在 Redis 实例很大的时候,重启将会十分缓慢。

在 Redis4.0 以后,提供快照+AOF日志的混合持久化的方式,将 RDB 的文件内容和增量的 AOF 日志存在一起。这里的 AOF 不再是全量的日志,而是自快照持久化开始到结束这段时间的数据修改指令日志。

Redis 在重启时,会先加载快照的数据,再对增量的 AOF 日志进行重放,完全替代之前的 AOF 全量重放,从而大大提高启动效率。

Comments