为什么要持久化数据???
Redis 的持久化主要有两大机制,即 AOF 日志和 RDB 快照。
操作前准备redis环境,我是docker 启动的
docker run -p 6380:6380 --name redis -v /home/tools/redis/notpassword/redis.conf:/etc/redis/redis.conf -v /home/tools/redis/notpassword/data:/data -d redis:6.0 redis-server /etc/redis/redis.conf --appendonly yes
注释掉 protected-mode no 与 bin 127.0.0.1 否则无法远程链接
原理
AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改
的指令记录,是先写内存,成功之后记录AOF日志
为了避免额外的检查开销,Redis 在向 AOF 里面记录日志的时候,并不会先去对这些命令进行语法检查。所以,如果先记日志再执行命令的话,日志中就有可能记录了错误的命令,Redis 在使用日志恢复数据时,就可能会出错。在命令执行后才记录日志,所以不会阻塞当前的写操作
Redis 在长期运行的过程中,AOF 的日志会越变越长,文件越来越大。如果实例宕机重启,加载 AOF 日志,重放整个 AOF 日志会非常耗时,导致长时间 Redis 无法对外提供服务,所以需要定期进行 AOF 重写,给 AOF 日志进行瘦身。
开启aop 后日志文件解读
执行命令 set name test 后查看appendonly.aof文件
# *3 表示当前命令有三个部分,每部分都是由“$+数字”开头
*3
# $3表示这部分中的命令、键或值一共有多少字节。
$3
set
# $4表示这部分中的命令、键或值一共有多少字节。
$4
name
# $4表示这部分中的命令、键或值一共有多少字节。
$4
test
AOF 机制提供了三个选择,也就是 AOF 配置项appendfsync 的三个可选值。
always
:同步写回:每个写命令执行完,立马同步地将日志写回磁盘;
everysec
:每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;
no
:操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。
Redis 提供了 bgrewriteaof 指令用于对 AOF 日志进行瘦身。其原理就是开辟一个子进程对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中。 序列化完毕后再将操作期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志文件了,瘦身工作就完成了。
实际上,重写机制具有“多变一”功能。所谓的“多变一”,也就是说,旧日志文件中的多条命令,在重写后的新日志中变成了一条命令。
如图中,这一条命令就能实现该数据的恢复,这就节省了五条命令的空间。对于被修改过成百上千次的键值对来说,重写能节省的空间当然就更大了。
重写机制和 AOF 日志由主线程写回不同,重写过程是由后台线程 bgrewriteaof 来完成的,这也是为了避免阻塞主线程
,导致数据库性能下降。
每次执行重写时,主线程 fork 出后台的 bgrewriteaof 子进程
。此时,fork 会把主线程的内存拷贝一份给 bgrewriteaof 子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof 子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志
。
因为主线程未阻塞,仍然可以处理新来的操作。此时,如果有写操作,Redis 会把这个操作写到它的缓冲区。这样一来,即使宕机了,这个 AOF 日志的操作仍然是齐全的,可以用于恢复。
这个操作也会被写到重写日志的缓冲区。这样,重写日志也不会丢失最新的操作。等到拷贝数据的所有操作记录重写完成后,重写日志记录的这些最新操作也会写入新的 AOF 文件,以保证数据库最新状态的记录。此时,我们就可以用新的 AOF 文件替代旧文件了。
redis客户端操作
可以重新打开appendonly.aof,所有命令都合为一条
如下两个配置可以控制AOF自动重写频率
# aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就 很快,重写的意义不大
#auto‐aof‐rewrite‐min‐size 64mb
# aof文件自上一次重写后文件大小增长了100%则再次触发重写
#auto‐aof‐rewrite‐percentage 100
所谓内存快照,就是指内存中的数据在某一个时刻的状态记录。这就类似于照片,当你给朋友拍照时,一张照片就能把朋友一瞬间的形象完全记下来。
对 Redis 来说,它实现类似照片记录效果的方式,就是把某一时刻的状态以文件的形式写到磁盘上,也就是快照。这样一来,即使宕机,快照文件也不会丢失,数据的可靠性也就得到了保证。这个快照文件就称为 RDB 文件,其中,RDB 就是 Redis DataBase 的缩写。
和 AOF 相比,RDB 记录的是某一时刻的数据,并不是操作,快照是一次全量备份,是内存数据的二进制序列化形式,在存储上非常紧凑,节省空间大小。
对于 Redis 而言,它的单线程模型就决定了,我们要尽量避免所有会阻塞主线程的操作
Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave
。
Redis RDB 文件生成的默认配置。
bgsave可以避免阻塞,但是避免阻塞和正常处理写操作并不是一回事。bgsave时,主线程虽然没有阻塞,可以正常接收请求,但是,为了保证快照完整性,它只能处理读操作,因为不能修改正在执行快照的数据。
此时 Redis 就会借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。
每次命令执行都会将所有redis内存快照到一个新的rdb文件里,并覆盖原有rdb快照文件。
简单来说,bgsave 子进程是由主线程 fork 生成的,可以共享主线程的所有内存数据。bgsave 子进程运行后,开始读取主线程的内存数据,并把它们写入 RDB 文件。此时,如果主线程对这些数据也都是读操作(例如图中的键值对 A),那么,主线程和bgsave 子进程相互不影响。但是,如果主线程要修改一块数据(例如图中的键值对 C),那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入 RDB 文件,而在这个过程中,主线程仍然可以直接修改原来的数据。
这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。
Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。
内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。这样一来,快照不用很频繁地执行,这就避免了频繁 fork 对主线程的影响。而且,AOF日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销。
在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可 以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。