Redis[2]-持久化

186 阅读7分钟

1.概述

持久化就是将内存中的数据写入非易失介质中,比如机械磁盘和SSD。 在服务器发生宕机时,作为内存数据库Redis里的所有数据将会丢失,因此Redis提供了持久化两大利器:RDB和AOF。

  • RDB 将数据库快照以二进制的方式保存到磁盘中。
  • AOF 以协议文本方式,将所有对数据库进行过写入的命令和参数记录到 AOF 文件,从而记录数据库状态。

2.RDB

2.1 RDB配置

[root@~ redis]# vim redis.conf

save 900 1 #表示每900秒钟有一条数据被修改则触发RDB
save 300 10
save 60 10000
dbfilename dump.rdb #指定把内存里的数据库写入本地文件的名称,该文件是进行压缩后的二进制文件;
dir ./ #指定RDB二进制文件存放目录 ;
# 持久化 rdb文件遇到问题时,主进程是否接受写入,yes 表示停止写入,如果是no 表示redis继续提供服务。
stop-writes-on-bgsave-error yes

前三行表示触发RDB的配置,只要任何一个条件满足就会触发bgsave命令RDB持久化 修改配置信息:

[root@~ src]# redis-cli 
127.0.0.1:6379> config get save
1) "save"
2) "900 1 300 10 60 10000"
127.0.0.1:6379> CONFIG SET save "21600 1000" 
OK

在命令行里进行配置,服务器重启才会生效,也可以修改redis.conf配置文件。

2.2 SAVE和BGSAVE

RDB持久化触发机制分为:手动触发和自动触发; 手动触发:

save:阻塞式持久化,执行命令时Redis主进程把内存数据写入到RDB文件中直到创建完毕,期间Redis不能处理任何命令。
bgsave:非阻塞式持久化,创建一个子进程把内存中数据写入RDB文件里同时主进程处理命令请求。

自动触发:

redis快照持久化相关配置
果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点
执行debug reload命令重写加载redis时

2.3 BGSAVE实现

RDB方式的持久化是通过快照实现的,符合条件时Redis会自动将内存数据进行快照并存储在硬盘上。

  • 调用glibc的函数fork产生一个子进程,快照持久化完全交给子进程来处理。
  • 父进程继续处理客户端请求。子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段,于操作系,在进程分离的一瞬间,内存的增长几乎没有明显变化。
  • 子进程做数据持久化,它不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。
  • 父进程持续服务客户端请求,使用操作系统的 COW 机制来进行数据段页面的分离(一页4k),当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据。

COW(copy on write)机制

fork:Unix操作系统上创建进程的主要方法。fork用于创建子进程(等同于当前进程的副本)。Linux下所有进程都是init进程(ppid)的子进程。 cow:写时复制机制,在父进程fork子进程,两个进程用的是相同的物理空间(内存区),此时内存设置成read only,写入操作时首先进行值拷贝,再对拷贝后的值执行写入操作,这样减少了无谓的复制耗时。

Redis在进行快照过程中不会修改RDB文件,只有快照结束后才会将旧的文件替换成新的,也就是任何时候RDB文件都是完整的。

2.4 RDB缺点

快照是通过开启子进程的当时进行,是一个比较耗资源的操作;
由于是子进程遍历内存数据持久化磁盘,其它操作对它是不可见的,导致持久化是内存数据的快照模式。

3.AOF(append only file)

AOF指的是存储Redis服务器的顺序指令序列的日志文件,AOF日志只记录对内存进行修改的指令。服务器可以通过顺序执行AOF日志文件所有的指令(重放)来恢复Redis当前实例的内存数据。 Redis在收到客户端的修改指令后,先进行参数校验、逻辑处理,如果没问题,就立即把该指令文本存储到AOF日志文件。

3.1 配置

#开启AOF持久化 默认no
appendonly yes 
#每次写入一条数据执行一次fsync,它能确保数据一条都不丢,但结果就是性能非常非常的差,吞吐量很低
# appendfsync always
#每隔一秒执行一次fsync,这个是最常用,生产环境一般都这么配置,性能很高,QPS还是可以上万的
appendfsync everysec
#不主动执行fsync ,redis将数据写入os cache就撒手不管了,然后后面os自己会时不时有自己的策略将数据输入磁盘,不可控了
# appendfsync no

3.2 AOF重写

Redis在长期的运行过程中,AOF日志会越来越长,如果重启,整个AOF日志重放会非常耗时,所以需要对Redis的AOF文件进行重写。

Redis提供了bgrewriteaof指令用于对AOF日志进行瘦身,通过开辟一个子进程对内存进行遍历,转换成一系列的Redis操作指令,序列化到一个新的AOF日志文件,完成后再讲操作期间的增量AOF文件追加到新的AOF日志文件中,最后替换旧的AOF文件。

触发方式: 手动执行bgrewriteaof指令 自动执行,设置一下参数:

auto-aof-rewrite-percentage 100 #当前的aof文件超过上一次重写后aof文件的1倍时才会再次rewrite auto-aof-rewrite-min-size 64mb #表示运行AOF重写时文件最小体积,默认为64MB

可以通过info Persistence指令查看redis的当前状态信息;

3.3 fsync

AO日志是以文件的形式存储的,当程序对AOF日志文件进行写操作时,实际上是将内容写到kernel为文件描述符分配(fd)的内存缓存当中,kernel会异步的即将缓存存储到磁盘,这就导致期间如果Redis宕机,AOF可能还未保存到磁盘,导致数据丢失。 Linux的glibc提供了fsync(int fd)函数可以将指定文件的内容强制从kernel缓存写到磁盘,因此Redis只要实时调用fsync函数就可以保证AOF日志不丢失,但是fsync是一个磁盘IO操作,如果一条指令执行一条fsync执行,会严重影响Redis的效率。 Redis提供了参数控制fsync的执行方式:

appendfsync everysec

appendfsync的参数有三个取值:

  • always #每次执行write指令后进行fsync
  • everysec #每秒执行一次fsync,一般用于生产配置
  • no #由操作系统自动调度何时fsync,性能最好

appendfsync & bgrewriteaof 如果同时执行AOF重写和fsync,两个都需要进行磁盘IO读写,且bgrewriteaof需要大量的磁盘操作,如果appendfsync值为always或everysec会一定程度上导致主进程写操作执行fsync阻塞,因此redis提供了参数进行控制:

no-appendfsync-on-rewrite no #取值为yes | no,yes表示在bgrewriteaof过程中将appendfsync设置成no,fsync只是将数据写入内核缓存,不会阻塞主进程指令,no表示允许写磁盘操作同是进行。

4.总结

两种持久化的方式各有优劣,对于RDB方式来说,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作,RDB文件紧凑,全量备份,数据恢复会比AOF快,但是会丢失大量数据。 AOF的方式可以通过fsync最大程度上保护数据不丢失,但是需要进行fsync同步磁盘效率上会比RDB慢,且AOF日志文件会比RDB数据快照文件更大,导致重启恢复数据时间更长。 在Redis4.0带来了一个新的持久化选项,混合持久化,将RDB文件和AOF日志放在一起,AOF日志文件不再是全量文件,而是保存RDB持久化过成功的增量文件。