Redis的数据都是存储在内存中的,内存中的数据在关机时会被清除,Redis持久化就是通过一定的机制将内存中的数据转储到磁盘上进行长期存储。
Redis提供了两种持久化机制:
- 1、RDB快照式持久化(全量备份)
- 2、AOF命令追加式持久化(增量备份)
RDB持久化
RDB持久化是指在客户端输入save
、bgsave
或者达到配置文件自动保存快照条件时,将Redis 在内存中的数据(二进制序列化形式)生成快照保存在名字为 dump.rdb(文件名可修改)的二进制文件中。
快照原理
Redis使用操作系统的多进程COW(Copy On Write)机制来实现快照持久化。
Redis在持久化时调用glibc函数fork产生一个子进程,持久化任务完全交给子进程处理,父进程继续处理客户端请求。在子进程刚刚产生时,它和父进程共享内存里的代码段和数据段。子进程不会修改现有的内存数据结构,只对数据结构进行遍历读取,然后序列化到磁盘中。父进程继续服务客户端请求。这个时候就会使用操作系统的COW机制来进行数据段的页面分离。数据段由很多操作系统页面组合而成,当父进程对其中一个页面的数据进行修改时,先将被共享的页面复制一份出来,然后对复制出来的页面进行修改。此时子进程相应页面没有发生变化,还是进程产生时的那一瞬间数据。
优点:
- RDB是一个非常紧凑(有压缩)的文件,它保存了某个时间点的数据,非常适用于数据的备份。
- 在恢复大数据集时,相比AOF来说RDB速度更快
- RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
缺点:
- Redis意外宕机 时,会丢失部分数据
- 当Redis数据量比较大时,fork的过程是非常耗时的,fork子进程时是会阻塞的,在这期间Redis 是不能响应客户端的请求的。
手动RDB持久化
save
127.0.0.16379> save
OK
save命令执行后,会阻塞redis进程,将内存中的数据转储到磁盘。在转储过程中,由于redis是单线程服务,在转储期间不能够响应其他命令。
bgsave
127.0.0.1:6379> bgsave
Background saving started
bgsave命令执行后:
1、服务器进程pid为14233派生出一个pid为14333的子进程
2、子进程将内存数据写入临时rdb文件
3、子进程完成写入后,将新的文件替换老的dump文件。
相比save命令,bgsave命令是异步的,不会影响服务进程响应其他命令。
自动RDB持久化
################################ SNAPSHOTTING ################################
# 触发自动保存快照
# save <seconds> <changes>
# save <秒> <修改的次数>
save 900 1
save 300 10
save 60 10000
# 设置在保存快照出错时,是否停止redis命令的写入
stop-writes-on-bgsave-error yes
# 是否在导出.rdb数据库文件的时候采用LZF压缩
rdbcompression yes
# 是否开启CRC64校验
rdbchecksum yes
# 导出数据库的文件名称
dbfilename dump.rdb
# 导出的数据库所在的目录
dir ./
AOF持久化
AOF持久化是通过记录Redis执行写命令来记录数据库状态。也就是每当执行一次写命令,这个命令就会被追加到AOF文件末尾。
AOF原理
AOF日志存储的是Redis服务器的顺序指令序列,AOF日志只记录对内存进行修改的指令记录。假设AOF日志记录了自Redis实例创建以来所有的修改性指令序列,那么就可以对一个空的redis实例顺序执行所有指令,达到重放的目的。来回复当前实例的内存数据结构的状态。
redis会在收到客户端修改指令后,先进行参数校验,校验通过后立即将该指令文本存储到AOF日志中,也就是先存盘后执行。这样即使遇到突发宕机也可以通过重放恢复之前的状态。
在redis长期运行过程中,AOF日志大小会不断增长。重启redis实例重放aof日志会非常耗时,导致长时间redis无法对外提供服务,所以需要对aof日志瘦身。
AOF持久化开启
appendonly yes
配置
############################## APPEND ONLY MODE ###############################
# 是否开启AOF功能
appendonly no
# AOF文件件名称
appendfilename "appendonly.aof"
# 写入AOF文件的三种方式
# appendfsync always
appendfsync everysec
# appendfsync no
# 重写AOF时,是否继续写AOF文件
no-appendfsync-on-rewrite no
# 自动重写AOF文件的条件
auto-aof-rewrite-percentage 100 #百分比
auto-aof-rewrite-min-size 64mb #大小
# 是否忽略最后一条可能存在问题的指令
aof-load-truncated yes
持久化过程
AOF持久化过程可以分为三个步骤:
1、命令追加
AOF持久化功能开启时,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾。此时缓冲区的记录还没有写入到appendonly.aof文件中。2、文件写入和同步
为了提高文件的写入效率,在现代操作系统中,当用户调用write函数,将一些数据写入到文件的时候,os通常会将写入数据暂时保存在一个内存缓冲区里面(例如,unix系统实现在内核中设有缓冲区高速缓存或页高速缓存,当我们向文件写入数据时,内核通常先将数据复制到缓冲区中,然后排入队列,晚些时候再写入磁盘),这种方式称为延迟写,等到缓冲区的空间被填满,或者超过了指定的时限,或者内核需要重用缓冲区存放其它磁盘块数据时,才会真正将缓冲区中的所有数据写入到磁盘里面。文件写入:只是写入到了内存缓冲区,可能还没有写到文件所拥有的磁盘数据块上
文件同步:将缓冲区中的内容冲洗到磁盘上
文件的写入和同步由配置文件中appendfsync参数控制。
appendfsync:
- always 每次有新命令时,就将缓冲区数据写入并同步到 AOF 文件
- everysec(默认) 每秒将缓冲区的数据写入并同步到 AOF 文件
- no 将缓冲区数据写入AOF 文件,但是同步操作到交给操作系统来处理
数据载入还原
- 创建一个不带网络连接的伪客户端
- 从AOF文件中分析并读取出一条写命令
- 使用伪客户端执行被读出的写命令
- 一直执行步骤2、3,知道AOF文件中的所有写命令都被处理完毕为止
AOF文件重写
由于AOF 持久化是通过不断地将命令追加到文件的末尾来记录数据库状态的, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大。 且有些命令是改变同一数据,是可以合并成一条命令的。就好比对一个计数器调用了 100 次 INCR,AOF就会存入100 条记录,其实存入一条数据就可以了。
所以为了处理这种情况,Redis提供了AOF重写机制。
AOF重写机制的触发有两种机制,一个是通过调用命令BGREWRITEAOF
127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started
另一种是根据配置文件中的参数触发,参数如下:
auto-aof-rewrite-percentage 100 #当前AOF文件大小和上一次重写时AOF文件大小的比值
auto-aof-rewrite-min-size 64mb #文件的最小体积
重写步骤(AOF重写不需要对现有的AOF文件进行任何读取、分析和写入操作)
- 创建子进程进行AOF重写
- 将客户端的写命令追加到AOF重写缓冲区
- 子进程完成AOF重写工作后,会向父进程发送一个信号
- 父进程接收到信号后,将AOF重写缓冲区的所有内容写入到新AOF文件中
- 对新的AOF文件进行改名,原子的覆盖现有的AOF文件
优点
- 使用AOF 会让你的Redis更加持久化
- AOF文件是一个只进行追加的日志文件,不需要在写入时读取文件。
- Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写 。
- AOF文件可读性高,分析容易
缺点
- 对于相同的数据来说,AOF 文件大小通常要大于 RDB 文件
- 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB
混合持久化
如果使用RDB持久化,在不满足持久化条件的情况下,重启可能会丢失一部分持久化数据。如果使用AOF日志重放,相对对rdb要慢很多,尤其数据量很多的情况下。
Redis4.0为了解决这个问题,带来了混合持久化选项。将rdb文件内容和aof日志文件存在一起,aof日志不再是全量日志,而是自持久化开始到持久化结束这段时间发生的增量aof日志。因此在redis重启时,可以先加载rdb内容,然后再重放aof日志,重启效率大幅提升。