redis 持久化AOF、RDB、混合持久化

为什么要持久化数据???

  1. Redis 的数据全部在内存里,如果突然宕机,数据就会全部丢失
  2. 即使只考虑缓存场景,如果未持久化,redis一旦服务宕机,内存中的数据将全部丢失。虽然可以从后端数据库恢复这些数据,但这种方式存在两个问题:一是,需要频繁访问数据库把缓存加载回来,会给数据库带来巨大的压力;二是,这些数据是从慢速数据库中读取出来的,性能肯定比不上从 Redis 中读取,导致使用这些数据的应用程序响应变慢。所以,对 Redis 来说,实现数据的持久化,避免从后端数据库中进行恢复,是至关重要的。

Redis 的持久化主要有两大机制,即 AOF 日志和 RDB 快照。
redis 持久化AOF、RDB、混合持久化_第1张图片
操作前准备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 否则无法远程链接

1、AOF日志

  • 原理
    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
    

1.1、三种写回策略

AOF 机制提供了三个选择,也就是 AOF 配置项appendfsync 的三个可选值。
redis 持久化AOF、RDB、混合持久化_第2张图片

  • always:同步写回:每个写命令执行完,立马同步地将日志写回磁盘;

    • 可以做到基本不丢数据,但是它在每一个写命令后都有一个慢速的落盘操作,不可避免地会影响主线程性能(生产环境基本不会使用,严重影响性能);
  • everysec:每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;

    • 采用一秒写回一次的频率,避免了“同步写回”的性能开销,虽然减少了对系统性能的影响,但是如果发生宕机,上一秒内未落盘的命令操作仍然会丢失。所以,这只能算是,在避免影响主线程性能和避免数据丢失两者间取了个折中。
  • no:操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。

    • 写完缓冲区后,就可以继续执行后续的命令,但是落盘的时机已经不在 Redis 手中了,只要 AOF 记录没有写回磁盘,一旦宕机对应的数据就丢失了;

redis 持久化AOF、RDB、混合持久化_第3张图片
一般来讲生产环境都是everysec

1.2、AOF 重写机制

Redis 提供了 bgrewriteaof 指令用于对 AOF 日志进行瘦身。其原理就是开辟一个子进程对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中。 序列化完毕后再将操作期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志文件了,瘦身工作就完成了。

实际上,重写机制具有“多变一”功能。所谓的“多变一”,也就是说,旧日志文件中的多条命令,在重写后的新日志中变成了一条命令。
redis 持久化AOF、RDB、混合持久化_第4张图片
如图中,这一条命令就能实现该数据的恢复,这就节省了五条命令的空间。对于被修改过成百上千次的键值对来说,重写能节省的空间当然就更大了。

重写机制和 AOF 日志由主线程写回不同,重写过程是由后台线程 bgrewriteaof 来完成的,这也是为了避免阻塞主线程,导致数据库性能下降。

每次执行重写时,主线程 fork 出后台的 bgrewriteaof 子进程。此时,fork 会把主线程的内存拷贝一份给 bgrewriteaof 子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof 子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志

因为主线程未阻塞,仍然可以处理新来的操作。此时,如果有写操作,Redis 会把这个操作写到它的缓冲区。这样一来,即使宕机了,这个 AOF 日志的操作仍然是齐全的,可以用于恢复。

这个操作也会被写到重写日志的缓冲区。这样,重写日志也不会丢失最新的操作。等到拷贝数据的所有操作记录重写完成后,重写日志记录的这些最新操作也会写入新的 AOF 文件,以保证数据库最新状态的记录。此时,我们就可以用新的 AOF 文件替代旧文件了。
redis 持久化AOF、RDB、混合持久化_第5张图片
redis客户端操作
redis 持久化AOF、RDB、混合持久化_第6张图片
可以重新打开appendonly.aof,所有命令都合为一条

如下两个配置可以控制AOF自动重写频率

# aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就 很快,重写的意义不大
#auto‐aof‐rewrite‐min‐size 64mb
# aof文件自上一次重写后文件大小增长了100%则再次触发重写
#auto‐aof‐rewrite‐percentage 100

2、RDB快照

所谓内存快照,就是指内存中的数据在某一个时刻的状态记录。这就类似于照片,当你给朋友拍照时,一张照片就能把朋友一瞬间的形象完全记下来。

对 Redis 来说,它实现类似照片记录效果的方式,就是把某一时刻的状态以文件的形式写到磁盘上,也就是快照。这样一来,即使宕机,快照文件也不会丢失,数据的可靠性也就得到了保证。这个快照文件就称为 RDB 文件,其中,RDB 就是 Redis DataBase 的缩写。

和 AOF 相比,RDB 记录的是某一时刻的数据,并不是操作,快照是一次全量备份,是内存数据的二进制序列化形式,在存储上非常紧凑,节省空间大小。

对于 Redis 而言,它的单线程模型就决定了,我们要尽量避免所有会阻塞主线程的操作

Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave

  • save:在主线程中执行,会导致阻塞;
  • bgsave:创建一个子进程,专门用于写入 RDB 文件,避免了主线程的阻塞,Redis RDB 文件生成的默认配置。

redis 持久化AOF、RDB、混合持久化_第7张图片

bgsave可以避免阻塞,但是避免阻塞和正常处理写操作并不是一回事。bgsave时,主线程虽然没有阻塞,可以正常接收请求,但是,为了保证快照完整性,它只能处理读操作,因为不能修改正在执行快照的数据。此时 Redis 就会借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。

每次命令执行都会将所有redis内存快照到一个新的rdb文件里,并覆盖原有rdb快照文件。

简单来说,bgsave 子进程是由主线程 fork 生成的,可以共享主线程的所有内存数据。bgsave 子进程运行后,开始读取主线程的内存数据,并把它们写入 RDB 文件。此时,如果主线程对这些数据也都是读操作(例如图中的键值对 A),那么,主线程和bgsave 子进程相互不影响。但是,如果主线程要修改一块数据(例如图中的键值对 C),那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入 RDB 文件,而在这个过程中,主线程仍然可以直接修改原来的数据。

redis 持久化AOF、RDB、混合持久化_第8张图片

这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。

3、混合持久化

Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。

内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。这样一来,快照不用很频繁地执行,这就避免了频繁 fork 对主线程的影响。而且,AOF日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销。

在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可 以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。

你可能感兴趣的:(redis,redis,redis持久化)