【Redis学习笔记】09 Redis 数据持久化

1. 持久化概述

Redis 是一个内存数据库,如果没有持久化功能,当 Redis 重启、宕机、掉电等情况发送时,所有存储在内存中的数据就会丢失,这种情况在某些应用场景下时不允许发生的,例如:(1)将 Redis 作为数据库使用;(2)将 Redis 作为缓存服务器,但是缓存被穿透后会对性能造成较大影响,所有缓存同时失效会导致缓存雪崩,从而使服务无法响应。

如果不使用持久化,那么当 Redis 重启后,所有的数据都会丢失

# Redis 重启之前有很多的 key
127.0.0.1:6379> KEYS *
1) "k3"
2) "k1"
3) "text"
4) "coupons"
5) "num1"
6) "k5"
7) "k2"
8) "k4"
127.0.0.1:6379> exit
# 当 Redis 重启之后,所以有的 key 全部丢失了
~]# systemctl restart redis-6379.service
~]# redis-cli 
127.0.0.1:6379> KEYS *
(empty list or set)

这时我们就希望 Redis 能将数据从内存以某种形式同步到磁盘中,使得重启后可以根据硬盘中的记录恢复数据,这一过程就是持久化。

Redis 支持 RDB 和 AOF 两种方式的持久化。前者会根据指定的规则定时将内存中的数据存储到磁盘上,后者在每次执行命令后将写数据的命令本身记录下来。两种持久化方式可以单独使用,也可以同时使用。

2. RDB 持久化

RDB 方式就类似快照,当符合一定触发条件的时候,Redis 会自动将内存中的所以有数据打一个快照,并存储在磁盘中

2.1. RDB 持久化优点

1. 压缩格式,RDB 文件紧凑
2. 全量备份,非常适合用于进行备份和灾难恢复
3. RDB 在恢复大数据集的数据时比 AOF 的恢复速度要快

2.2. RDB 持久化缺点

1. 不是实时的,可能会丢数据
2. 操作比较重量(看 RDB 持久化原理)

2.3. RDB 持久化原理

  1. 当 RDB 满足触发条件时,Redis 会调用系统函数 fork(),创建一个子进程进行持久化
  2. 子进程将数据集写入到一个临时的 rdb文件中,当子进程完成对临时 rdb 文件的写入时,Redis 用新的 rdb 文件替换原来旧的 rdb 文件,并将旧的 rdb 文件删除

2.4. RDB 触发方式

命令 概述
save 触发方式 该命令会阻塞当前 Redis 服务器,执行 save 命令期间,Redis 不能处理其他命令,直到 RDB 过程完成为止
bgsave 触发方式 执行该命令时,Redis 会在后台异步进行快照操作,快照同时还可以响应客户端请求
配置文件自动触发 save m n:表示 m 秒内数据集存在 n 次修改时,自动触发 bgsave。
stop-writes-on-bgsave-error 默认值为 yes。当启用了 RDB 且最后一次保存数据失败,Redis 是否停止接收数据。
rdbcompression 默认值为 yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。
rdbchecksum 默认值为 yes。在存储快照后,我们还可以让 Redis 使用 CRC64 算法来进行数据校验,但是这样做会增加大约 10% 的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
dbfilename 设置快照的文件名,默认时 dump.rdb
dir 设置快照文件的存放路径,这个配置项一定是一个目录,而不能是文件名。

2.5. save 和 bgsave 对比

命令 save bgsave
IO 类型 同步 异步
阻塞? 是(阻塞发生在 fork)
复杂度 O(n) O(n)
优点 不会消耗额外内存 不阻塞客户端命令
缺点 阻塞客户端命令 需要 fork,消耗内存

2.6. 配置 RDB 参数

2.6.1. 普通参数

# 这里save后面第一参数表示时间(秒),第二参数表示改变量,合在一起的意思是多少秒内发生多少改变量后才保存一次
save 900 1
save 300 10
save 60 10000

# 保存快照数据使用的文件名
dbfilename redis.rdb

# 快照文件保存的路径
dir /data/redis/6379

为什么有 3 个 save 呢?是为了保障在不同情况下数据保存文件的最小化

2.6.2. 高级参数

# 后台备份进程出错时,主进程停不停止写入?主进程不停止容易造成数据不一致
stop-writes-on-bgsave-error yes

# 导出的rdb文件是否压缩,如果rdb的大小很大的话建议这么做
rdbcompression yes

# 导入rdb恢复数据时,要不要检验rdb的完整性,验证版本是不是一致
rdbchecksum yes

2.7. RDB 持久化操作

RDB 持久化不是实时保存数据,需要一个过程

2.7.1. 配置 RDB 持久化

~]# cat /opt/redis/6379/etc/6379.conf
daemonize yes
bind 127.0.0.1 192.168.66.51
port 6379
pidfile /opt/redis/6379/pid/6379.pid
logfile /opt/redis/6379/logs/6379.log
dir /data/redis/6379
dbfilename 6379.rdb
save 900 1
save 300 10
save 60 10000

修改完成配置文件之后,重新启动 Redis

systemctl restart redis-6379.service

2.7.2. 创建测试数据是否生成rdb文件

# 1. 创建测试数据
~]# redis-cli 
127.0.0.1:6379> KEYS *
(empty list or set)
127.0.0.1:6379> SET k1 v1
OK
127.0.0.1:6379> SET k2 v2
OK
# 2. 观察并未生成 rdb 文件
 ~]# ll /data/redis/6379/
total 0

# 3. 手动执行 bgsave
127.0.0.1:6379> BGSAVE
Background saving started

# 4. 再次观察,发现生成了 rdb 文件
~]# ll /data/redis/6379/
total 4
-rw-r--r-- 1 redis redis 111 Mar 10 17:38 6379.rdb
# 5. 批量创建数据,触发生成 rdb 文件
~]# for i in {1..15000};do redis-cli set k_${i} v_${i}; echo "${i} is ok";done

# 6. 观察,发现生成了新的 rdb 文件
~]# ll /data/redis/6379/6379.rdb 
-rw-r--r-- 1 redis redis 103273 Mar 10 17:43 /data/redis/6379/6379.rdb

# 7. 查看日志,可以看到触发了 save 300 10
~]# tailf /opt/redis/6379/logs/6379.log 
2076:M 10 Mar 2022 17:43:56.065 * 10 changes in 300 seconds. Saving...
2076:M 10 Mar 2022 17:43:56.066 * Background saving started by pid 9185
9185:C 10 Mar 2022 17:43:56.190 * DB saved on disk
9185:C 10 Mar 2022 17:43:56.191 * RDB: 4 MB of memory used by copy-on-write
2076:M 10 Mar 2022 17:43:56.213 * Background saving terminated with success
2076:M 10 Mar 2022 17:48:57.109 * 10 changes in 300 seconds. Saving...
2076:M 10 Mar 2022 17:48:57.110 * Background saving started by pid 17187
17187:C 10 Mar 2022 17:48:57.474 * DB saved on disk
17187:C 10 Mar 2022 17:48:57.474 * RDB: 2 MB of memory used by copy-on-write
2076:M 10 Mar 2022 17:48:57.551 * Background saving terminated with success

# 8. 执行shutdown命令
127.0.0.1:6379> SHUTDOWN
not connected> 

# 9. 观察日志
~]# tailf /opt/redis/6379/logs/6379.log
2076:M 10 Mar 2022 17:52:33.694 # User requested shutdown...
2076:M 10 Mar 2022 17:52:33.694 * Saving the final RDB snapshot before exiting.
2076:M 10 Mar 2022 17:52:33.705 * DB saved on disk
2076:M 10 Mar 2022 17:52:33.705 * Removing the pid file.
2076:M 10 Mar 2022 17:52:33.705 # Redis is now ready to exit, bye bye...

# 启动 redis
~]# systemctl start redis-6379
# 获取有多少个key,可以发现数据没有丢失
~]# redis-cli dbsize
(integer) 15002

2.8. RDB 持久化结论

  1. 配置了dir 和 dbfilename 参数,但是没配置save参数时:
    a. shutdown 不会持久化保存数据
    b. 可以手动执行 bgsave 命令进行持久化
  2. 配置 save 参数时:
    a. shutdown/kill/pkill 均会自动触发 bgsave 持久化保存数据
    b. pkill -9/kill -9 不会触发持久化(慎用)
  3. 恢复数据时:
    a.持久化数据文件要和配置文件中定义的路径和名称一样才能被识别
    b. RDB 持久化只有一个数据文件,迁移和备份只要这一个 rdb 文件即可

注意:RDB 高版本兼容低版本,低版本不能兼容高版本,例如:3.X的 rdb 文件可以导入到 5.X 的版本中,但是 5.X 的 rdb 文件不能导入到 3.X 的版本中

3. AOF 持久化

AOF 会记录服务执行的所有写操作命令,并在服务启动时,通过重新执行这些命令来还原数据集

3.1. AOF 持久化优点

1. AOF的策略是每秒钟或者每次发生写操作的时候都会同步,因此即使服务器故障,最多只会丢失1秒的数据。 
2. AOF存储的是Redis命令,并且是直接追加到aof文件后面,因此每次备份的时候只要添加新的数据进去就可以了。
3. 如果AOF文件比较大了,那么Redis会进行重写,只保留最小的命令集合。

3.2. AOF 持久化缺点

1. AOF文件因为没有压缩,因此体积比RDB大。 
2. AOF是在每秒或者每次写操作都进行备份,因此如果并发量比较大,效率可能有点慢。
3. AOF文件因为存储的是命令,因此在灾难恢复的时候Redis会重新运行AOF中的命令,速度不及RDB。

3.3. AOF 持久化流程

3.4. AOF 持久化操作

3.4.1. 配置 AOF 持久化

# 2. 修改配置文件内容
 ~]# cat /opt/redis/6379/etc/6379.conf
daemonize yes
bind 127.0.0.1 192.168.66.51
port 6379
pidfile /opt/redis/6379/pid/6379.pid
logfile /opt/redis/6379/logs/6379.log
dir /data/redis/6379
appendonly yes
appendfilename "6379.aof"
appendfsync everysec
# 为了方便观察 AOF 持久化关闭 RDB处就会
#dbfilename 6379.rdb
#save 900 1
#save 300 10
#save 60 10000

# 3. 清理数据目录中的 rdb 文件
~]# ll /data/redis/6379/
total 4
-rw-r--r-- 1 redis redis 379 Mar 12 13:18 6379.rdb
~]# rm -f /data/redis/6379/6379.rdb 
~]# ll /data/redis/6379/
total 0

# 4. 重写启动redis
~]# systemctl restart redis-6379.service

3.4.2. 批量创建 key 并观察 aof 文件

# 1. 批量创建 15000 个 key
~]# for i in {1..15000};do redis-cli set k_${i} v_${i}; echo "${i} is ok";done
~]# ll -h /data/redis/6379/
total 512K
-rw-r--r-- 1 redis redis 508K Mar 12 13:35 6379.aof

3.4.3. 重启服务数据是否丢失

# 1. 关闭 Redis
~]# redis-cli 
127.0.0.1:6379> DBSIZE
(integer) 15000
127.0.0.1:6379> SHUTDOWN
not connected> 

# 2. 启动 Redis
~]# systemctl start redis-6379

# 3. 查看数据是否丢失
~]# redis-cli 
127.0.0.1:6379> DBSIZE
(integer) 15000

3.4.4. 观察 aof 文件内容

aof 文件中会记录 Redis 的所有更命令。删除、新增 key 都会记录在 aof 文件中,当 Redis 重启后,会将 aof 文件中的 命令还原到 Redis 中,从而保证数据的持久化。

~]# tail -10 /data/redis/6379/6379.aof 
k_14999
$7
v_14999
*3
$3
set
$7
k_15000
$7
v_15000

3.5. AOF 重写机制

redis是如何解决aof文件持续增大的问题的?

AOF 的特点就是记录服务运行过程中中产生的所有命令,如果又有 del 命令,当 AOF 文件的大小达到限制是,会触发内部的压缩命令,将 del 命令删除以及 del 之前创建的相关 key 从文件中删除,当服务再次重启时,重新执行这些命令来还原数据

执行的命令        aof记录            redis的数据       
set k1 v1        set k1 v1          k1                
set k2 v2        set k1 v1          k1 k2
                 set k2 v2          
set k3 v3        set k1 v1          k1 k2 k3
                 set k2 v2
                 set k3 v3      
del k1           set k1 v1          k2 k3
                 set k2 v2
                 set k3 v3
                 del k1
del k2           set k1 v1          k3
                 set k2 v2
                 set k3 v3
                 del k1
                 del k2
实际有意义的只有一条记录:
set k3

官方配置文件中内容:

# Automatic rewrite of the append only file.
# Redis is able to automatically rewrite the log file implicitly calling
# BGREWRITEAOF when the AOF log size grows by the specified percentage.
#
# This is how it works: Redis remembers the size of the AOF file after the
# latest rewrite (if no rewrite has happened since the restart, the size of
# the AOF at startup is used).
#
# This base size is compared to the current size. If the current size is
# bigger than the specified percentage, the rewrite is triggered. Also
# you need to specify a minimal size for the AOF file to be rewritten, this
# is useful to avoid rewriting the AOF file even if the percentage increase
# is reached but it is still pretty small.
#
# Specify a percentage of zero in order to disable the automatic AOF
# rewrite feature.

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

配置的aof文件大小为64m,默认就是64m,当文件超过64m时,redis有自己的重写策略,会把aof中的删除命令全部删掉,保证文件的大小,也可以根据实际生产把文件大小给大。

4. AOF 和 RDB 读取实验

4.1. 实验背景

aof和rdb同时存在,redis重启会读取哪一个数据?

4.2. 实验步骤

# 1. 创建测试数据,并手动保存 rdb 文件
set k1 v1
set k2 v2
bgsave    # rdb 保存, k1 v2

# 2. 移动 rdb 文件
mv 6379.rdb /opt

# 3. 清空aof文件
flushall

# 4. 再次创建测试数据
set k3 v3
set k4 v4  # aof 保存 k3 k4

# 5. 移动 aof 文件
mv 6379.aof /opt

# 6. 停止 Redis, 清理持久化文件
redis-cli shutdown
rm -f /data/redis/6379/*

# 7. 移动之前的 rdb 和 aof 文件到持久化目录
mv /opt/redis.aof  /data/redis/6379
mv /opt/redis.rdb  /data/redis/6379

# 8. 启动服务,并查看 key
systemctl start redis

4.3. 实验结论

当 aof 和 rdb 同时存在时,重启 Redis 会优先读取 aof 的内容

5. 如何选择 RDB 还是 AOF

官网推荐:https://redis.io/topics/persistence
1. 开启混合模式
2. 开启aof
3. 不开启RDB,不设置触发器
4. rdb采用定时任务的方式定时备份
5. 可以从库开启RDB进行备份

你可能感兴趣的:(【Redis学习笔记】09 Redis 数据持久化)