Redis的持久化机制
Redis的持久化
Redis 的读写都是在内存中,所以它的性能较高,但在内存中的数据会随着服务器的重启而丢失,为了保证数据不丢失,我们需要将内存中的数据存储到磁盘,以便 Redis 重启时能够从磁盘中恢复原有的数据,而整个过程就叫做 Redis 持久化。
Redis的可持久化也是redis和memcached的主要区别之一,Memcached没有持久化机制。
Redis持久化的方式
一、快照方式RDB
RDB(Redis DataBase),某一个时刻,将当前内存中存储的所有数据形成一个快照,并以二进制形式写入磁盘。
二、文件追加AOF
AOF(append only file),将操作命令,以文本追加的形式写入文件。
三、混合持久化方式
Redis4.0以后支持的方式,由于AOF的文件占用空间大,而RDB的文件占用空间小,但是RDB丢数据比较多,所以有了这种AOF和RDB混合的持久化优化方式,触发时会将当前的内存快照转为RDB文件,但是后续记录的仍是AOF的逐条写入。这样既可以让持久化文件变得小一些,可以提高重启时数据恢复的速度。同时AOF机制也能降低RDB机制的数据丢失风险。
RDB
将某一时刻的内存快照,以二进制的形式写入磁盘,生成dump.rdb文件。
触发方式
手动触发
手动触发有两种方式:save
和bgsave
命令
-
save
命令:在客户端执行save就会触发redis的持久化,这种方式会阻塞redis的主线程。
127.0.0.1:6379> save OK
-
bgsave
命令:是save的后台版本,fork()一个子进程执行save,可以立即返回,不会阻塞主线程。
127.0.0.1:6379> bgsave Background saving started
自动触发
有三种情况可以进行自动触发:
-
配置参数
save m n
:设置如果在m秒内,如果有n个键发生改变,就执行bgsave命令触发RDB持久化;这个命令可以设置多个,多个之间是或的关系,即任意一个条件满足都会触发持久化。
-
执行命令
flushall
:flushall
命令用于清空redis数据库,执行这条命令时,如果redis配置开启了持久化,就会在清空前,执行一次持久化操作。 -
主从同步时触发:
当从节点执行全量复制操作时,主节点会执行一次
bgsave
命令,生成rbd文件,并将rdb文件传给从节点。
自动触发的配置参数可以写在redi的配置文件中:
# RDB 保存的条件
save 900 1
save 300 10
save 60 10000
# bgsave 失败之后,是否停止持久化数据到磁盘,yes 表示停止持久化,no 表示忽略错误继续写文件。
stop-writes-on-bgsave-error yes
# RDB 文件压缩
rdbcompression yes
# 写入文件和读取文件时是否开启 RDB 文件检查,检查是否有无损坏,如果在启动是检查发现损坏,则停止启动。
rdbchecksum yes
# RDB 文件名
dbfilename dump.rdb
# RDB 文件目录
dir ./
- save参数:配置自动触发RDB持久化的条件
- rdbcompression:默认是yes,表示开启RDB文件压缩,redis会采用LZF算法进行压缩
- rdbchecksum:默认是yes,表示写入文件和读取文件时是否开启RDB文件检查,如果有损坏,在redis启动时会终止启动
可以通过在客户端执行config get [参数名]
命令来查看redis的某个参数值。
配置的设置方式:
- 手动修改redis的配置文件redis.conf,永久有效,但是需要重启redis
- 使用config set [参数名] “参数值” 的方式进行运行时修改,但是这种方式重启redis后会丢失。比如要禁用redis持久化,可以用命令
config set save ""
。
数据恢复
当redis服务器启动时,如果redis的根目录下有dump.rdb文件,则会自动加载该文件并恢复数据。可以通过redis的启动日志查看是否有加载数据。
RDB的优缺点
优点
- 二进制的数据存储,占用磁盘空间小,更适合做备份文件,对容灾恢复很有用,并且加载数据的速度也很快,redis重启时速度快。
- RDB的自动持久化机制会fork一个子进程,不会阻塞主进程。
缺点
- RDB是根据配置的策略进行持久化的,redis挂掉会丢失上次持久化到现在的数据量。
- RDB需要fork子进程进行持久化,且会加载内存中的所有数据,如果数据集很大,fork的操作可能很耗资源,虽然持久化的过程是不阻塞主线程的,但是创建子进程的时间可能会偏长,甚至高达几毫秒到一秒。
RDB的COW机制
copy-on-write
COW(copy-on-write)写时复制机制,是一种当多个线程需要操作同一份数据时,减少内存分配的机制。
在Redis主线程fork一个子进程执行bgsave
或者bgrewriteaof
命令时,子进程会和父进程共享一个内存空间,如果父进程此时没有写操作,那么子进程就可以顺利读取数据写入磁盘,只有在父进程发生写操作修改内存数据时,才会真正去分配内存空间并复制数据,而且也只是复制被修改的内存页中的数据,并不是全部的内存数据;
COW机制的主要好处是
- 减少分配和复制资源时带来的瞬时延迟
- 减少不必要的内存分配
可以看出,COW机制在写少读多时很有效,但是如果写操作变多,那复制的性能就会变差。
所以如果是dict出现rehash时,那么写入操作将是不可避免的,在执行bgsave
或者bgrewriteaof
创建子进程生成RDB文件时,为了提高COW的效率,dict的负载因子阈值就会提升到5,以减少写入操作。
COW机制在Go的原子操作中也有很多应用。
AOF
AOF(Append Only File)中文是附加到文件,顾名思义 AOF 可以把 Redis 每个键值对操作都记录到文件(appendonly.aof)中。
AOF的配置
查看是否开启AOF
通过命令config get appendonly
可以查看到redis是否开启了AOF:
127.0.0.1:6379> config get appendonly
1) "appendonly"
2) "no"
开启AOF持久化
Redis 默认是关闭 AOF 持久化的,想要开启 AOF 持久化,有以下两种方式,命令行或者配置文件:
-
修改配置文件,进行以下设置,完成后要重启redis:
appendonly yes
-
运行时在命令行修改配置,只在一次启动过程中生效:
config set appendonly yes
AOF触发持久化的方式
AOF持久化也包括自动触发和手动触发两种
自动触发
两种条件可以触发AOF,满足AOF设置的触发策略,或者满足AOF重写时,都会触发AOF
-
满足AOF设置的触发策略
AOF的触发策略通过参数
appendfsync
设置:127.0.0.1:6379> config get appendfsync 1) "appendfsync" 2) "everysec"
这个参数有三个可选值:
- always:每条redis的写入操作都会写入磁盘,如果redis宕机,最多丢失一条数据,这种性能较差,很少人用,因为每次写都要操作磁盘,就失去了redis作为内存数据库的优势。
- everysec:每隔一秒写入一次磁盘,最多会丢失一秒的数据。
- no:不设置写入的规则,根据当前操作系统决定何时写入磁盘,linux的话默认是30s写入一次。
最长使用的就是默认值everysec。
-
满足AOF重写时触发
AOF重写:由于AOF是不停的追加写入语句到dump.aof文件的,所以这个文件会越来越大,为了缩小这个文件的体积,可以对文件中的一些写入语句进行合并,已达到缩小文件体积的目的。
重写的实现:触发AOF重写需要满足两个参数
auto-aof-rewrite-min-size
:允许AOF重写的最小dump.aof文件大小,默认是64MB。auto-aof-rewrite-percentage
:AOF重写的大小比例,默认是100%,即只有当前的AOF文件,比最后一次的AOF文件大一倍时才触发重写(加入测试时每秒同步一次,就意味着这一秒里插入了大量的数据)。
AOF重写的流程:先生成一个新文件,然后根据旧的aof文件写入新文件,合并其中一些操作,然后交换两个文件,删掉旧的文件即可。
在触发重写时会先立刻进行一次AOF持久化
手动触发
命令行执行bgrewriteaof
即可手动立刻触发一次aof写入。
127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
数据恢复
dump.aof文件同样是保存在redis服务的根目录,也同样是随着redis启动时自动加载,并恢复数据。
持久化文件的加载规则:
- 如果只开启了AOF,redis启动时只会加载aof文件。
- 如果只开启了RDB,则只会加载rdb文件。
- 如果同时启动了AOF和RDB,则会只加载aof文件。
持久化文件截断的问题
在AOF写入时如果redis挂掉,那么最后一条记录会被截断,那么重启redis时这条数据就无法恢复,并且会导致redis无法启动,可以设置参数aof-load-truncated=yes
,来让redis启动时忽略最后一行的错误数据,而正常启动。
如果是AOF文件中间的部分数据出现截断的现象,上面的参数就没有用了,可以使用两种方式解决:
- 使用aof修复工具,命令行输入
redis-check-aof
,会跳转到aof文件的错误的那一行,然后可以手动修复。 - 如果无法手动修复,可以使用
redis-check-aof --fix
命令,自动修复aof文件,这样会导致错误数据之后的所有数据都被丢弃。
AOF的优缺点
优点
- 相比rdb,aof的数据更完整,一般都采用1s刷一次磁盘的策略,最多只会丢1s的数据
- 追加的方式写入,如果出现异常,也可以通过上面两种方式修复。
- aof文件易于人类理解,如果使用了
flushall
命令清空了数据库,那么只需要将aof文件中最后一行的flushall
命令删除,然后重启redis即可恢复,rdb文件因为是一个时间的内存快照则是没用。
缺点
- aof文件比rdb文件大得多。
- 在redis负载较高时,RDB效率更高,因为AOF会阻塞主线程。
- RDB是拷贝内存快照,而AOF是追加写入磁盘,理论上说RDB更不容易出现错误。
混合持久化方式
Redis4.0后增加了混合持久化的方式,结合rdb和aof的优点,redis5.0以后这个是默认开启的。
配置混合持久化
同样有两种方式可以开启混合持久化:
-
命令行方式:
config set aof-use-rdb-preamble yes
-
配置文件方式
aof-use-rdb-preamble yes
混合持久化的实现方式
在进行AOF重写时,会将当前的内存快照以RDB的形式写入aof文件,而后续则继续以aof的格式一条条书写。这样文件大小就大大见减小了,同时rdb的数据恢复也会更快。
这样aof文件就会是这样:
数据恢复
同样的,redis会加载相同目录下的dump.aof文件,然后前面rdb的部分就用rbd的方式恢复,后面aof的部分就用aof的方式恢复。
混合持久化方式的优缺点
优点
结合了rdb和aof的优势,让redis的数据恢复更快,同时减小了文件大小,并且还能尽量减少数据丢失,和资源消耗。
缺点
- aof文件的可读性消失了
- 不能向前兼容,只有redis4.0以后的版本能用
关于Redis持久化的一些技巧
- 持久化能保证数据不丢,但同时也影响了Redis的性能,所以需要一个平衡。
- 数据丢失不敏感的情况下,可以关掉持久化功能,这样的话redis和memcached差别就不那么大了,甚至memcached的吞吐还要好于redis。
- 可以主从部署,可以让一台redis作为写入,但不开启持久化,从redis只读数据,并且开始持久化。
- 尽量使用混合持久化的方式。
- Redis对cpu要求不高,但是内存和磁盘的快慢对redis影响很大,所以尽量用比较好的内存和磁盘。