1. 持久化
由于 Redis 中的数据是存储在 内存 中的,每次重启进程/主机 内存中的数据就都会丢失,要持久数据就要放到硬盘上,持久化就是避免 数据丢失 的手段。
Redis 持久化机制有两种:1)RDB :Redis Database 和 2)AOF:Append Only File 两种
RDB :定期备份,隔一段时间备份一下;
AOF:实时备份,只要有变动,就备份一下;
1.1 RDB
1.1.1 前置知识
RDB机制------定期将 Redis 中的所有数据,写入硬盘,生成一个 "快照"(文件),Redis 重启后,通过该快照恢复数据。
Redis 默认是开启了 RDB 的。
这个 "快照",是一个二进制文件 ,内存中的数据是压缩保存 在里面的(消耗 cpu 资源,但是省空间),存储在 redis 的工作目录中,名为 dump.rdb(在 redis 配置文件中,可进行设置位置和名字)
redis.conf 即配置文件

默认名称:

默认位置:

不要乱改 "快照" ,Redis 启动时,会尝试加载该 rdb 文件,如果文件格式错了,就会加载失败,Redis启动失败 ,即使成功启动也有数据错风险。
当即使你不主动改它,它也可能有问题,例如 通过网络传输 该 rdp 文件时,丢数据了,也会造成 rdb 文件错误
redis 提供了 rdb 文件的检查工具 redis-check-rdb,在 /bin 下(注意是 / 目录下,不是 root):

当前版本的Redis,检查工具 和 redis服务器 是同一个可执行程序,运行时加入不同选项,就有不同的功能。
使用该命令时,加上 rdb 文件,就是检查工具。
运行生成 "快照" 时,会把生成的数据先存在一个临时文件中,当 "快照" 生成完毕后,会删除之前的 rdb 文件,并将临时文件改名为 dump.rbd。
执行 flushall 的时候,会连带 rbd 文件也清空
1.1.2 触发机制
RDB 触发机制,分为两种:
1)手动触发 :
通过 save 或 bgsave 命令,触发快照生成。
save:阻塞式执行,没完成,redis不执行其他客户端的命令(不推荐,数据量大的时候,阻塞时间太长)
bgsave :bg 即 background,不影响 Redis 服务器处理其他客户端的命令。
之前初阶中,讲到 Redis 是单线程的,因此这里并发编程的操作,是通过多进程实现的

执行 bgsave,Redis 父进程先判断是否有子进程正在执行保存(如 RDB/AOF 子进程),存在则直接返回。
若不存在,则父进程执行 fork 命令创建子进程,fork 执行期间会阻塞父进程,fork 执行完毕后返回 Background saving started ,不再阻塞父进程。
子进程完成快照的生成,并对原有文件进行原子替换,之后进程发消息给父进程表示完成。
fork 创建的子进程是父进程的拷贝,但不是直接复制出一份一模一样的,而是 写时拷贝,即子进程与父进程持有同一份内存,相当于引用,子进程的权限是只读,当父进程的对数据进行了改动,才会真正拷贝,父进程持有新数据的那一部分(变动的部分才拷贝)
2)自动触发 :
在 Redis 的配置文件中设置的,隔多长时间且有多少次改动 就触发。
默认触发规则:

过了 900 秒,且这 900s 内至少有 1 个 key 改动,就生成快照,剩下的 300 和 60 规则同理。
如果是 save "" ,则表示没有规则了,不自动保存了。
当然,这里也可以自己手动设置,但是不能太频繁,因为快照生成是比较耗性能的,成本比较高的操作。
也正因不能太频繁,所以快照中的数据和当前实际的数据可能存在偏差。
除此之外,
2)通过 shutdown,restart 等命令关闭,重启 redis 服务器,也会触发 "快照" 生成
3)redis 进行主从复制时,主节点也会自动生成 rdb 快照,然后将 快照内容 传输给节点
bgsave 操作流程是创建子进程,子进程完成持久化,用新文件替换旧文件。(如果是 save 命令,则不会触发 子进程 & 文件替换,会直接往文件里写)
这里数据量太少,持久化速度太快,难以观察到子进程,但是可以观察到 新文件替换旧文件,在使用 stat 命令查看 dump.rdb 的 inode 编号即可( inode 相当于 文件的身份证)

Linux 文件系统
文件系统典型组织方式(ext4),将整个文件系统分成了三大部分
- 超级块:放一些管理信息
- incode 区:存放 inode 节点,每个文件都会分配一个 inode 数据结构(含文件各种元数据)
- block 区:存放文件数据内容
redis 服务的日志 放在 /var/log/redis 下,启动不成功时可以,看一下。
1.1.3 总结
RDB 适用于备份,全量复制等场景,如每六小时 bgsave 备份,并将 RDB 文件复制到远程机器或文件系统中,用于灾备。
RDB 文件使用特定二进制格式保存,Redis 不同版本中 RDB 版本可能不同,有兼容风险。
1.2 AOF
1.2.1 前置知识
AOF 即 append onlf file。
AOF 是使用 文本 的方式组织数据的,需要进行一系列的字符串切分操作。所以,Redis 加载 RDB 恢复数据的速度要快于 AOF(RDB 是二进制 方式组织数据,直接将数据读到内存中,按字节的格式取出放到 结构体/对象 中即可)。
aof 类似于 MySQL 的 binlog 会将 用户每个操作 记录到文件中,而不是数据。
默认 aof 是关闭的,需要在配置文件打开。
开启 aof 后,aof 文件的优先级要高于 rdb 文件。即 有 aof 就用 aof 中的数据来恢复,没有 aof 才用 rdb 中的数据进行恢复。
如果有 aof 文件,但是 aof 文件有问题,如被乱改了,则redis启动失败。
1.2.2 开启 & 使用

把这里的 no 改为 yes,并重启 redis 服务,就开启了 AOF 模式。
在 redis 中进行如下操作:

aof 是文本文件,默认位置和 rdb 相同,以 .aof 结尾。

文件中通过一些特殊符号作为分隔符,对命令细节做区分。
1.2.3 AOF 对 Redis 性能的影响
Redis 速度快,是因为只操作内存,现在开启了 AOF 既要 写内存,又要写硬盘,Redis 性能是否受到了影响?
实际上,没有影响到 redis 处理请求的速度。
- AOF 不是直接将数据写硬盘的,而是先写入 内存中的一个缓冲区,积累一定后,统一写入。
写硬盘时,写入次数比写入量 对性能的影响大多了,举个例子:你在卧室嗑瓜子,垃圾桶在客厅,磕完一把在跑到客厅扔,不你磕一个就到客厅扔一次,要更省时。
- 硬盘的顺序读写是要较随机读写快的,AOF 每次将新操作写入原有文件末尾,属于顺序写入。
由于数据是先写入缓冲区(内存)的,如果在缓冲区刷新,写入硬盘前,服务器出现了掉电等情况,这些数据就都没有了。
这就导致, 刷新频率越高,性能影响越大,数据可靠性也高;频率越低,影响越小,可靠性低。
为此 Redis 给出了一些选项,让我们自己选
| 配置值 | 说明 |
|---|---|
| always | 命令写入 aof_buf 后就 fsync (即写入硬盘),完成后返回。频率最高,可靠性最高,性能最低 |
| everysec | 命令写入 aof_buf 后,只执行 write 操作, 不 fsync,每秒由同步线程进行 fsync。频率降低,可靠性降低,性能提高 |
| no | 命令写入 aof_buf 后只执行,write,由 os 控制 fsync 频率。频率最低,可靠性最低,性能最高 |
这里的 write 是 将数据从 redis 进程中的内存(缓冲区) 转交给 操作系统内核,上面的 os 就是指操作系统。
redis 是应用程序,没有直接操作硬盘的权限,所有的 硬盘 I/O 都必需通过 操作系统完成。
1.2.4 AOF 重写机制
随着用户操作的增多,aof 文件的体积会越来越大,这就会影响 redis 下次启动的时间(redis 启动时,要读 aof 文件)。
而且,aof 文件中是存在冗余内容的。举个例子:用 string 类型 的 key1 记录,商品数量,期间多次售卖和补货,但是 redis 恢复时,也只恢复到关闭前的最终状态,即 key1 最终值。这些用于减少和增加的命令,在文件里就是多余的。
因此,redis 引入了 重写机制,对 aof 文件进行整理,剔除冗余操作,合并一些操作,来给 aof 文件瘦身。
触发:
- 手动触发:使用 bgrewriteaof 命令
- 自动触发:根据配置文件中
auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定的触发时机auto-aof-rewrite-min-size:触发重写时 AOF 最小文件大小,默认 64 MBauto-aof-rewrite-percentage:当前 AOF 占用大小较上次重写时的增长比例
流程:

父进程收到重写命令,fork 创建出子进程(和 rdb 创建机制一致)。
同时,父进程会再创建一个 aof_rewrite_buf 缓冲区,用来放 fork 后的命令。
创建完子进程,子进程写新的 aof 文件, 父进程仍可以接收客户端的新命令,并将数据写入 aof_rewrite 和
aof_rewrite_buf 缓冲区。aof_rewrite 缓存区的数据,依旧遵循规则,写入旧 aof 文件。(还写旧的文件,是为了应对一些特殊情况,如重写的时候服务挂了,写的这期间收到的命令就没了,新的aof 内容不完整,redis 重启后数据也不完整。)
当子进程完成新 aof 文件后,会通过信号通知父进程,父进程再把 aof_rewrite_buf 缓冲区 中的内容写入 新 aof 文件,之后用 新 aof 文件 替换掉 旧的。
注:重写时,不关注原 aof 文件里都有什么,只关注 内存中的最终数据状态,子进程只需要将 内存中的数据 获取处理,以 aof 的格式写入新 aof 文件即可(内存中的即是最终状态)。
如果,执行 bgrewriteaof 时,redis 已正在进行 aof 重写,则不再执行 aof 重写,直接返回。
如果,执行 bgrewriteaof 时,redis 已正在进行 rdb 文件生成,则会等 rdb 生成完,在执行 aof 重写。
子进程通过 信号 通知 父进程 的 信号, 可以认为是 linux 的 神经系统,进程之间相互作用(通信)的一种手段,信号表达的信息优先,不能像 socket 那样传输任意数据,只传像这里的 子进程对父进程表示 "干完了" 的信息。
1.2.5 混合持久化
aof 本来是按 文本 格式写入文件的,但是前面提到过 文本 的加载成本较高,所以,redis 引入了 混合持久化 的方式,即 按照 aof 的文本 格式,记录每一个请求/操作,触发 aof 重写后,当前内存状态(重写的结果)按 rdb 的二进制写入新的 aof 文件,后续依旧按 aof 的 文本 格式写数据。
是否启用 混合持久 可以在配置文件中进行配置,默认为 yes ,即启用
