Redis作为一个内存数据库,数据是以内存为载体存储的,即所有数据都保存在内存中。一旦Redis服务器进程退出或宕机,即便重启redis服务,数据也会全部丢失。为了解决这个问题,Redis提供了持久化机制,说白了就是把数据保存到磁盘上,当redis重启后,可以从磁盘中恢复数据!
★ Redis提供2种持久化方案:
一个是快照的方式,一个是类似日志追加的方式
RDB 就是 Redis DataBase 的缩写,中文名为快照/内存快照。RDB是一种快照存储持久化方式,原理就是将Redis某一时刻的内存数据写入到RDB文件中,并保存到磁盘上。
该文件是一个压缩过的二进制文件,默认文件名为dump.rdb
,通过该文件可以还原redis数据库的数据。在Redis服务器启动时,会重新加载dump.rdb文件的数据到内存当中,达到恢复数据目的。
★ 可根据 redis.conf 配置文件中设置rdb文件名 和 保存目录:
# 指定本地数据库文件名(rdb文件的名称),默认值为dump.rdb
dbfilename dump.rdb
# 指定本地数据库存放目录(dump.rdb文件存放目录),rdb、aof文件也会写在这个目录
dir /usr/local/var/db/redis/
——既然RDB持久化的方式是生成RDB文件,那么RDB文件是怎么生成的呢?
触发RDB文件生成的方式:
- 手动触发:通过命令手动生成快照
- 自动触发:通过配置参数的设置触发自动生成快照
执行
save
和bgsave
命令,可以手动触发快照,生成RDB文件
当某个客户端发送 save 命令后,此时会阻塞当前Redis服务器,在RDB文件创建完成之前是不能处理其他客户端发送的任何命令请求,如果数据量太大会造成长时间阻塞,期间redis无法处理其他请求,线上环境不建议使用。如下图所示:
当某个客户端发送 bgsave 命令后,Redis服务器会执行fork() 函数创建一个子进程,由子进程负责rdb文件创建和写入,期间服务器不会阻塞,可以接受其他客户端的命令请求,但在fork执行阶段,服务器还会阻塞,无法接受其他客户端命令(一般时间很短!)如下图所示:
✸ 具体流程如下:
注意:bgsave/bgrewriteaof(rdb/aof) 的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。
✶ 简化后的流程可分为三步:
bgsave命令
,主线程需调用系统的 fork()
函数,构建一个子进程去操作;bgsave
命令执行结束Redis服务器在处理bgsave采用子线程进行IO写入,而主进程仍然可以接收其他请求,但forks子进程是同步阻塞的,在forks子进程阶段,不能接收其他请求,如果forks一个子进程花费的时间太久(一般是很快的),bgsave命令仍然有阻塞其他客户的请求的情况发生
在以下几种情况时会自动触发生成RDB文件:
特别注意:flushall 是删库跑路,生成一个空的dump.rdb文件,目的是覆盖并删除上次备份产生的dump.rdb,防止重启Redis时,数据又会恢复到上一次备份的时候的数据!!
▶ shutdown命令
1. 首先,先删除rdb文件!
连接redis客户端使用 config get dir 命令,获取rdb文件的保存路径,再打开新的命令窗口,删除 rdb文件(也可以直接删除,确保没有此文件即可)
2. 删除完rdb文件后,使用shutdown命令断开客户端连接,会自动触发rdb持久化,如下图:
3. redis重启后,会重新加载dump.rdb文件的数据到内存当中,达到恢复数据目的!
redis.conf 配置文件不懂的,可以翻阅我之前redis栏目中关于配置文件的详解,附有每个参数的中文详细说明!
# 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
# 语法:save
save 900 1 # 900秒(15分钟)内至少1个key值改变
save 300 10 # 300秒(5分钟)内至少10个key值改变
save 60 10000 # 60秒(1分钟)内至少10000个key值改变
1. 跟 shutdown 命令展示的一样,先删除dump.rdb文件!再打开redis.conf 文件进行如下配置:
2. 打开redis服务,并用命令连接客户端,设置3个key,让其触发save条件
3. 等待60秒后,查看dump.rdb文件目录:
4.等dump.rdb文件生成完毕后,断开客户端,并再次重连客户端,测试数据是否恢复!
flushall 生成的是空dump.rdb文件,目的是覆盖并删除上次备份的快照,防止redis重启时,自动载入并恢复到上一份备份数据!
☁ 思考:可以每秒做一次快照吗?
在未达到save的触发条件,此时服务器宕机,则会丢失最后一次同步的时间段内的数据,那是否可以设置save,每几秒或每秒就触发备份条件,不就能大大减少数据的丢失吗?
bgsave执行不阻塞主线程,但频繁地执行全量快照,也会带来两方面的开销:
bgsave备份时如数据量太大,内存中的数据同步到硬盘的过程会持续比较长的时间,在这段时间Redis服务一般还会收到其他客户端的写操作请求。那么如何保证数据一致性呢?
RDB中的核心思路是Copy-on-Write
在正常的快照操作中,Redis主进程会fork一个子进程来处理rdb文件的写入,此时Redis服务还会接受其他客户端包括写请求在内的任何命令。
在子进程执行备份阶段,和redis主进程接受其他客户端写请求阶段,这段时间发生的数据变化会以副本的方式存放在另一个新的内存区域,待快照操作结束后才会同步到原来的内存区域。
★ 工作流程如下所示:
☛ 解析:
AOF(append only file)持久化:与RDB存储某个时刻的快照不同,AOF是将客户端的每一个写操作命令都记录到日志中,追加到后缀为aof 的文件末尾,在Redis服务器重启时,会加载并运行aof文件的所有命令,以达到恢复数据的目的。
AOF文件是怎么生成的呢?
bgrewriteaof
命令(该命令会重新aof,下面有讲)
★ 手动触发,如下图所示:
★ 自动触发
默认情况下,Redis是没有开启AOF的,可以通过配置 redis.conf 文件来开启AOF持久化:
#---------------------------1.写入---------------------------
# appendonly参数开启AOF持久化 no:关闭(默认) yes:开启
appendonly no
# 指定aof文件名,默认为appendonly.aof
appendfilename "appendonly.aof"
# AOF持久化三种同步策略:
# 1) no:Redis服务器不负责写入aof,由操作系统来处理何时写入aof。性能好,不安全(不推荐)
# 2) always:每个写操作都会追加到appendonly.aof,可靠性高,数据基本不丢失(慢,安全)
# 3) everysec:每秒同步一次到appendonly.aof,会导致丢失这1s数据(折中选择,默认值)
appendfsync everysec
AOF工作流程图解如下:
✦ 解析:
aof_buf
缓冲区中。appendfsync
参数决定。AOF持久化策略的效率与安全性:
- always:把每个写命令都立即同步到aof文件,很慢,但是很安全
- everysec:每秒同步一次,Redis官方推荐。
- no:redis服务器不执行写入磁盘,而是交给OS系统来处理,非常快,但是也最不安全
1. 首先打开redis.conf 配置文件,在附加模式这栏中,修改appendonly属性为yes
配置文件设置完毕后,记得重启redis服务 !!!或者先关闭服务才修改配置文件!
2. 把目录下的aof 和 rdb文件都删除掉(不知道目录的可以通过config get dir 获取)
3. 重启服务后,重新连接客户端,执行写入操作
4. 查看appendonly.aof 文件!
每一个写入命令都被追加到aof文件中!
其实想要从这些文件中恢复数据,只需要重新启动Redis即可。如图:
✦ 解析:在上图中,通过重启服务,达到恢复数据的目的,成功获取到了数据!
——那么恢复后,我们继续执行set 写入操作呢?再次查看 aof 文件:
结论:可以看到,写入操作又被添加到aof 的末尾,而之前已经恢复的数据的写入命令也还在!
数据的备份、持久化做完了,我们如何从这些持久化文件中恢复数据呢?如果一台服务器上有既有RDB文件,又有AOF文件,该加载谁呢?如下图:
✦ 解析:启动时会先检查AOF文件是否存在,如果不存在就尝试加载RDB
那么为什么会优先加载AOF呢?因为AOF保存的数据更完整,通过上面的分析我们知道AOF基本上最多损失1s的数据。
开启AOF后,每一次写操作都会被追加到appendonly.aof,随着时间推移,AOF文件会越来越大。数据恢复也更加慢。如果不加以控制,会对Redis服务器,甚至对操作系统造成影响。为了解决AOF文件体积膨胀的问题,Redis提供AOF文件重写机制来对AOF文件进行“瘦身”。
★ 举例说明:比如对一个key多次执行incr命令
incr num 1
incr num 2
incr num 3
incr num 4
...
incr num 100000
aof 模式会把每次命令都追加到appendonly.aof 文件中,文件过大,redis服务器重启恢复数据时,就会非常慢。如果此时对aof文件重写,可以生成一个恢复当前数据的最少命令集,比如上面的例子中那么多条命令,可以重写为:
set num 100000
自动触发:在redis.conf配置文件中,进行配置如下参数:
#---------------------------2.重写---------------------------
# 触发重写配置
# 当AOF文件的体积>64MB,且文件的体积比上次重写后的体积大了一倍(100%),会执行bgrewriteaof
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 如aof文件被损坏,是否开启自动修复
aof-load-truncated yes
如果要手动触发,可直接调用bgrewriteaof命令
★ 图解触发重写机制后的整体流程如下:
流程如下:
思考:父进程为什么同时向 aof_buf 和 aof_rewrite_buf 两个缓冲区写入数据?
答:在AOF重写日志期间发生宕机(即子进程重写过程中发生意外),新aof文件还没来得及替换旧的,故恢复数据时,用的还是旧的aof文件,即写入aof_buf 缓冲区的数据。
aof_rewrite_buf 缓冲区:重写期间,由于父进程还可接受其他客户端的写请求,新接受的写请求,需放到重写缓存区aof_rewrite_buf中,子进程重写完毕,父进程会把aof_rewrite_buf重写缓冲区的数据追加到新aof文件中,确保数据的统一
★ 简化后的流程如下:
- AOF的rewrite重写 和 RDB的bgsave命令 都是由父进程fork 出一个子进程来执行
- 重写是直接把当前内存的数据生成对应命令,并不是读取旧AOF文件进行命令合并
☁ 思考:重写过程中,主进程哪些地方会阻塞?
☁ 思考:为什么AOF重写不复用旧AOF文件
redis 启动服务如果存在aof 文件就不会去加载rdb 文件了,而是以aof 文件来恢复数据,如果aof 文件损坏了,那会出现什么样的结果呢?
通常aof 文件错误有两种:
写入错误:在写入的过程中,出现写入操作被打断,磁盘空间不足,或者磁盘出现物理故障等问题。
停机故障:在写入的过程中,发生 Redis 服务器进程别强制杀死,或者服务器的宿主机器被强制停机等情况
★ 假设我们对已经存在的aof 文件做人工损坏
1. 使用 vim 命令进入编辑aof 文件
我是Mac系统用的终端(命令同Linux系统),所以下面讲解就用命令方式操作,windowns用户用cmd,或者找到目录直接鼠标右键打开
2. 按 “ i ” 键进入编辑模式,出现 insert 字样表示当前可编辑
3. 将正确的值搞乱之后,再退出保存如下:
4. 切换到redis 客户端,断开连接,重连服务和客户端
✦ 解析:如上图所示,提示连接失败!如果在启动 Redis 时, 用户试图将带有不完整数据的 AOF 文件提供给 Redis 时, Redis 将会拒绝使用用户所提供的 AOF 文件来还原数据, 因为错误的 AOF 文件会破坏数据的一致性。
Redis 附带的 redis-check-aof
可以解决这类因为 AOF 出错而无法启动的问题: 它可以移除 AOF 文件所包含的不完整的数据, 使得 Redis 可以重新载入 AOF 文件中的数据。
1、具体操作步骤如下:
程序会定位到出错的地方, 并询问用户, 是否要对文件进行截断, 如果选择
y
的话, 就会对指定的 AOF 文件进行修复。
2、修复成功之后,我们再来用vim 命令查看aof 文件的内容
可以看到,出错的原因 —— 那个不完整的 SET 命令已经被删除了。
3、测试结果
方式 | RDB | AOF |
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 会丢数据 | 由策略决定 |
轻重 | 重 | 轻 |
✓ 优点
✕ 缺点
✓ 优点
✕ 缺点
✦ RDB持久化
在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
RDB 是存放数据库中数据,适合做数据备份,但数据可能不全,最近几分钟的数据可能没有
✦ AOF持久化
以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录。
每秒执行一次,如果有写操作的命令就存储起来,最多丢失1秒的数据,适合做数据恢复。不适合做数据备份,由于每秒都会执行多少会抢占redis的内存,影响性能。