Redis之持久化
Redis支持数据持久化,Redis会将当前内存中的数据以特定的格式持久化到磁盘中,防止数据丢失,而且持久化的数据可以做备份和数据恢复。Redis提供了两种持久化机制,RDB和AOF
RDB
原理流程
RDB为Redis Database的缩写,也就是Redis的数据库。RDB的原理其实就是Redis达到了触发持久化的条件以后,Redis的主线程立刻创建一个fork子进程,然后子进程创建RDB临时文件,将主进程当前时间的内存全量数据写入RDB临时文件,写入完成以后,将磁盘上就的RDB文件替换为新的RDB文件。以下是具体的原理流程
- 触发持久化
- Redis主进程开始执行持久化,首先主进程判断当前是否存在正在执行的save进程或者bgsave进程,如果存在则直接返回,不存在则fork一个子进程,创建子进程完成之前,Redis主进程进入阻塞状态,不能对外提供服务。Redis子进程创建完毕之后,主进程继续执行并对外提供服务。
- fork出来的子进程创建
.RDB
临时文件,由于fork出来的子进程与主进程共享内存空间,所有子进程利用cow(CopyOnWrite)
机制将主进程当前时间节点的内存数据全量写入自己的进程中并写入.RDB
文件 - 子进程将磁盘上旧的
.RDB
持久化文件替换为新的.RDB
文件
触发条件
-
主动:主动触发其实也就是任务的运行命令来触发RDB持久化。
- save:采用同步方式,save为运行完成的时候,会阻塞Redis,无法对外提供服务。不推荐
- bgsave:采用异步的方式,bgsave后台开启独立的线程,不会阻塞Redis。
-
被动:被动的方式是通过
redis.conf
配置文件配置来触发RDB持久化。在redis.conf
中找到如下配置,根据自己业务场景自行配置。#3600秒有一个key变化则触发RDB持久化 save 3600 1 #300秒有一个key变化则触发RDB持久化 save 300 100 #60秒有一个key变化则触发RDB持久化 save 60 10000
-
特殊方式触发
- 服务器运行期间重启执行
debug reload
- 关闭服务器是保存指定数据。运行
shutdown save
- 服务器运行期间重启执行
优点
- 支持压缩,存储效率高
- 恢复数据速度快
缺点
- 实时性不高,可能存在数据丢失的风险
- 可能导致性能下降,如果内存中数据量特别大,一次持久化可能很消耗性能
- 增加额外开销。因为需要fork子进程
- redis每个版本的RDB文件的格式没有统一的标准,不同版本的redis的数据恢复可能存在问题。
AOF
AOF全称Append only file,从名称上我们可以看出是将数据追加的方式写入文件中。Redis的AOF指的是Redis的操作命令以独立日志文件的形式进行存储,Redis重启或者恢复的时候重新执行AOF文件中的操作命令达到数据的恢复与持久化。AOF主要弥补了RDB实时性不高的缺点,保证了Redis持久化的实时性。
原理
AOF的原理是当遇到一个对Redis写操作的时候,Redis会将该命名写入内中的aof_buffer
中,Redis会根据AOF配置的同步策略决定什么时间将aof_buffer
中的数据写入磁盘。
Redis启动的时候会检查AOF文件是否存在,如果不存在则创建(默认appendonly.aof
),如果存在则读取该文件进行内存数据的恢复。值得大家注意的是,.aof
文件是redis在启动的时候创建的,如果在redis运行期间删除.aof
文件,则redis的AOF就会失效。
AOF默认为关闭状态。可以根据以下配置进行开启。
appendonly yes
appendfilename "appendonly6379.aof"
# AOF持久化时机(fsync()函数可以通知操作系统立刻向硬盘写数据)
# always: 每次写操作都调用fsync()将缓冲区中数据写入并同步到aof文件中,由于需要同步磁盘,所以这种方式相对缓慢,但安全性要高。
# everysec:每秒执行一次fsync写入aof文件,等待磁盘同步,即能应对较高并发,且只存在丢失最后一秒数据的情况。
# no:不执行fsync,但通知操作系统,由系统在需要的把缓冲区中的数据写入aof中,不等待磁盘同步,所以这种方式是最快的,但安全性却也最差,是否丢失及丢失的数据量无法确定。
appendfsync everysec
# 理解这个参数需要先了解bgrewriteao重写机制,和bgsave类似bgrewriteao会在一个子进程中去进行aof的重写,从而不会阻塞主进程对其它命令的处理。
# 但是由于bgrewriteaof通常都会涉及到大量的磁盘操作,而且持续时间也比较长,当父进程也需要操作磁盘时,两者就可能产生竞争,从而可能导致父进程停顿,这个参数的出现就是为了解决这个问题。
# 设为yes的情况下相当于将appendfsync设置为no,并不会立刻执行磁盘操作,而只是写入了缓冲区,因此这样并不会造成阻塞,但是如果这个时候redis挂掉,就会丢失数据。(在Linux系统中默认最多会丢失30s的数据)
# 默认no的情况下,不会产生丢失,但却有可能阻塞。目前官方建议除非延迟到影响性能的程度,否则应该采用默认no。
no-appendfsync-on-rewrite no
# 这里配置的AOF重写的两个触发条件:1)当文件内容是上一次rewrite后的一倍 2)且文件大小大于64M时触发。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 设置为yes, Redis在加载aof文件时,如果aof文件中的最后一条命令不完整或者有误,redis会自动截取只成功加载前面正确的数据。
# 设置为no,那么Redis将启动失败,这种情况需要手动用redis-check-aof 工具对aof文件进行修复。
aof-load-truncated yes
重写机制
Redis AOF重写机制主要是为了解决AOF过大的问题。AOF的重写机制的原理是主进程for看一个子进程,子进程将当前时刻Redis内存中的全量数据已Redis命令的形式存储在一个新的AOF文件中,并替换掉旧的AOF文件。
在AOF重写的过程中也会出现以下两种问题。
- 由于Redis在执行AOF重写的时候,可能数据量很大,所以持续的时间比较长,这样新来的redis操作命令子进程无法知道,可能丢失新来的命令
- 由于子进程共享的是主进程的内存,子进程会抢占主进程的资源,导致Redis对外提供服务性能下降。
对于Redis AOF重写出现的两个问题,Redis已经帮我们解决了。
- 针对命令丢失的问题解决办法为:Redis AOF持久化正常进行。再次基础之上也为重写也提供一块缓冲区,所有新来的redis操作命令也往这个缓冲区里写,当子进程重写完成以后将重写缓冲区的数据生成aof文件,并替换到旧的aof文件,然后子进程通知主进程重写完成。
- 针对重写子进程导致redis服务性能下降的问题解决办法为:Redis提供了一个配置
no-appendfsync-on-rewrite no
,设为yes的情况下相当于将appendfsync设置为no,并不会立刻执行磁盘操作,而只是写入了缓冲区,因此这样并不会造成阻塞,但是如果这个时候redis挂掉,就会丢失数据。默认no的情况下,不会产生丢失,但却有可能阻塞。目前官方建议除非延迟到影响性能的程度,否则应该采用默认no。
重写规则
- 进程已超时的数据不写入文件
- 无效指令不写入文件
- 对同一条数据的多个指令合并成一条指令
触发方式
-
手动触发。运行
bgrewriteaof
-
自动重写。在配置文件中配置如下两个参数。然后根据
aof_current_size
和aof_base_size
进行公式计算自动触发重写。具体公式如下:auto-aof-rewrite-min-size size auto-aof-rewrite-percentage percentage
aof_current_size>auto-aof-rewrite-min-size (aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewrite-percentage
CopyOnWrite
CopyOnWrite是一种子进程操作主进程内存空间的一种机制。
该机制的原理是当主进程fork了一个子进程以后,Kernel(linux内核)
就将主进程中的所有内存页的权限设置成read-only
,然后子进程的内存地址空间指向主进程的内存地址空间,都对主进程中的内存进行写操作的时候,CPU会检测内存页的权限为read-only
,于是触发也异常中断,陷入一个kernel中断例程,中断例程中,kernel就会异常中断的页复制一份,于是父子进程就会单独持有一份。