AOF vs RDB 对比

redis 是一个内存型数据库,之前在《Go 语言+Redis 实战课》中签到的案例,数据是放到 redis 中,还有秒杀的案例,部分数据也是放到了 redis 中,如果这个时候,redis 故障了,redis 重新启动以后,内存中的数据肯定就全部消失了,用户是抱怨的,老板肯定也是不接受的。那 Redis 也提供了数据持久化到磁盘上的能力,就可以避免数据丢失了。

Redis 提供了两种方式保存数据数据的方式:

  1. RDB
  2. AOF

面试知识点梳理

RDB 要学什么?

什么是持久化,什么是快照?RDB 的工作原理以及优点和缺点是什么?

AOF 要学什么?

为什么要有 AOF?

如何选择 RDB 和 AOF,如何互补?

单独开启 RDB?单独开启 AOF? 还是 2 个一起开始,那 2 个都开启,数据冗余怎么考虑?

AOF 存储的是一个文件,这个文件有上限吗?

存储上限到了,怎么办?

要重写,为什么要重写?

重写的触发条件是什么?

常用的配置是什么?

文件如何写入?文件损坏了,怎么办?

redis 在运行中,开启的是 rdb,在 redis 不重启的情况下,数据不丢失的情况下,如何完成动态切换?

redis 的备份容灾,如何恢复?如何更好的恢复?

redis 如何提高性能? 从硬盘角度出发

Redis 的持久化方式

  1. RDB 的全称是 Redis database。顾名思义,RDB 就是将 Redis,用来存储数据的,所以通过 RDB 方式持久化就是将存在 Redis 内存中的数据写入到 RDB 文件中保存到磁盘上,从而实现持久化的。
  2. RDB 数据库使用操作日志记录每个操作,以防失败后通过日志恢复一致性。操作日志是按顺序追加写入的,所以不会出现无法恢复操作日志的情况。(类似 MySQL 的重做和撤销日志)。RDB 是指定时间间隔对数据进行快照存储,类似 MySQL 的 dump 备份文件。
  3. AOF 方式,数据库不修改旧数据,只是通过追加写入,因此数据本身就是日志,所以不会出现数据无法恢复的情况。记录每一次对服务器写操作,服务器重新启动的时候会重新执行这些命令来恢复原始的数据(类似 MySQL 的 binlog)

RDB 操作

redis.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 端口
port 6379
# 关闭保护模式
protected-mode no
# 开启 appendonly 备份模式
appendonly yes
# 每秒钟备份
appendfsync everysec
# 对aof文件进行压缩时,是否执行同步操作
no-appendfsync-on-rewrite no
# 当目前aof文件大小超过上一次重写时的aof文件大小的100%时会再次进行重写
auto-aof-rewrite-percentage 100
# 重写前AOF文件的大小最小值 默认 64mb
auto-aof-rewrite-min-size 64mb

# 日志配置
# debug:会打印生成大量信息,适用于开发/测试阶段
# verbose:包含很多不太有用的信息,但是不像debug级别那么混乱
# notice:适度冗长,适用于生产环境
# warning:仅记录非常重要、关键的警告消息
loglevel notice
# 日志文件路径
logfile "/data/redis.log"

docker-compose.yml

1
2
3
4
5
6
7
8
9
redis:
container_name: my-redis
image: redis:7.0.4
restart: always
ports:
- "6379:6379"
volumes:
- ./redis-conf/redis.conf:/etc/redis/redis.conf
- ./redis-data/:/data

我们用管道的方式插入数据(之前的案例中,介绍过),我们看一下截图

02.redis数据查看

pipeline插入数据截图

在 redis 内部,运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
info memory

"# Memory
used_memory:113723248
used_memory_human:108.45M
used_memory_rss:118013952
used_memory_rss_human:112.55M
used_memory_peak:113744208
used_memory_peak_human:108.47M
used_memory_peak_perc:99.98%
used_memory_overhead:81681400
used_memory_startup:842648
used_memory_dataset:32041848
used_memory_dataset_perc:28.39%
allocator_allocated:113852744
allocator_active:121044992
allocator_resident:123273216
total_system_memory:2085158912
total_system_memory_human:1.94G
used_memory_lua:37888
used_memory_lua_human:37.00K
used_memory_scripts:0
used_memory_scripts_human:0B
number_of_cached_scripts:0
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:1.06
allocator_frag_bytes:7192248
allocator_rss_ratio:1.02
allocator_rss_bytes:2228224
rss_overhead_ratio:0.96
rss_overhead_bytes:-5259264
mem_fragmentation_ratio:1.04
mem_fragmentation_bytes:4331752
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_clients_slaves:0
mem_clients_normal:61536
mem_aof_buffer:0
mem_allocator:jemalloc-5.1.0
active_defrag_running:0
lazyfree_pending_objects:0
lazyfreed_objects:0
"

可以看到内存使用情况。如果没有持久化,那么当 redis 发生故障,那么内存中保存的数据就会消失,这让业务使用方是无法接受的。

在 redis.conf 中,添加如下

1
2
# 5秒有1个key改动,执行快照生成
save 5 1

如果我们把 5 秒改成 900 秒,必须要等到这个时间到了,才能执行,不利于测试。

接下来重启 redis,我们运行

1
2
3
docker-compose down

docker-compose up -d

然后我们再看 dump.rdb 文件,100 万条数据是占硬盘 33.8mb(主要也是单条数据大小决定的,也就是你的存储对象有多大决定的)。

可以停止 redis 服务,然后再次重新加载,我们观察,数据都还在,这是因为,启动的时候都会去加载 rdb 文件,把内容读取到内存。

缺点:如果我们把 5 秒,改成 900 秒,也就是 15 分钟,那么在这段时间 redis 故障,我们就会损失掉 15 分钟的数据,这个时间是很长的,对于数据使用方,也是无法接受的,可以通过 AOF 方式做补充。

Redis 默认对 RDB 保存的时间

1
2
3
4
5
6
#900秒内如果超过一个key改动,触发保存快照
save 900 1
#300秒内如果超过10个key改动,触发保存快照
save 300 10
#60秒内如果超过1万个key改动,触发保存快照
save 60 10000

什么是 Redis 快照

刚刚说的 dump.rdb,我们就称为快照文件。

快照产生的情况,如下:

  1. 手动执行 bgsave。(非阻塞:当前线程仍然可以对外提供服务)
  2. 手动执行 save。(阻塞:由于 redis 是单线程,所以当前线程不能提供服务。)
  3. 根据配置文件自动执行。
  4. 客户端发送 shudown 命令,系统会先执行 save 命令,然后关闭服务器。(由于服务器即将关闭,所以不会接收新的请求)
  5. 主从架构的时候,从服务器向主服务器发送 sync 命令执行复制操作的时候,主服务器会执行 bgsave。(主节点会先做 bgsave,然后生成 rdb,然后通过 sync 命令把 rdb 发送给从节点)

RDB 原理

源码在 rdb.c 中可查看。

3.rdb原理

RDB 优点:

  1. 二进制,高效,备份简单,可以把 rdb 文件备份到其他 hdfs,fasthdfs 中等。数据文件在硬盘上占用也不是很大。
  2. fork 子进程,优化了性能
  3. 启动效率高。

RDB 缺点:

  1. 生成快照时机。生产环境一般 15 分钟或 30 分钟, 这个时间内没有生成快照,然后 redis 故障了,就会丢数据。(下面 AOF 弥补这个缺点)
  2. fork 子进程开销不确定性。如果数据集很大,频繁 fork,会造成性能问题。

AOF 持久化

AOF 全称 Append Only File,意思只追加文件,在每次处理完请求命令后将这个命令追加到 aof 文件末尾。默认 aof 是关闭的,开启如下:

1
2
3
4
5
6
7
8
9
10
11
# 开启 appendonly 备份模式
appendonly yes
appendfilename "appendonly.aof"
# 每秒钟备份
appendfsync everysec
# 对aof文件进行压缩时,是否执行同步操作
no-appendfsync-on-rewrite no
# 当目前aof文件大小超过上一次重写时的aof文件大小的100%时会再次进行重写
auto-aof-rewrite-percentage 100
# 重写前AOF文件的大小最小值 默认 64mb
auto-aof-rewrite-min-size 64mb

生成了 appendonly.aof 以后

1
2
3
4
5
6
7
8
9
10
11
12
*2
$6
SELECT
$1
0
*3
$3
set
$1
h
$1
h

*数字 代表下面几行是命令

$数字 代表命令或值的字节长度

1
2
3
4
5
6
7
*3
$3
set
$1
y
$1
y

*3 代表下面 3 行是命令。

$3 指 set 这个命令的字节长度

$1 分别是 key 和 value 的字节长度。

看到这里,可以尝试删除 rdb 文件,然后重新启动 redis,数据还在 Redis 中。

AOF 同步策略

  1. 每秒同步(默认,每秒调用一次 fsync)
  2. 每次修改同步(会极大削弱 Redis 的性能,这种模式下每次 write 后都会调用 fsync)
  3. 不主动同步(操作系统决定什么时候写入磁盘)

rdb 是固定一个时间,线上环境比如 15 分钟同步一次,但是 aof 是每秒同步一次,这个效率上肯定差异巨大。

AOF 工作原理

AOF原理

AOF 备份与恢复

AOF 文件是一个只进行 append 操作的日志文件,写入时,出现故障或宕机,不会破坏已经写入的数据内容。即使写入一半数据出问题了,在 redis 启动之前,可以使用 redis-check-aof 工具修复即可。

AOF 对人类友好且容易读懂的。文件保存了对 redis 执行的所有操作,这些写入操作按 redis 协议的格式保存。导出 AOF 文件也很简单。如果不小心执行 FlushAll 命令,只要 AOF 文件未被重写,停止服务器,移除 AOF 文件末尾的 FlushAll 命令,重新 redis,就可以将数据都恢复到 FlushAll 之前的状态了。

重写

随着 Redis 的 AOF 文件越来越大,自动地在后台对 AOF 进行重写(Rewrite)。

默认配置

1
2
auto-aof-rewrite-min-size 64mb 当文件超过64mb就重写
auto-aof-rewrite-percentage 100 按百分比,大于上次文件的2倍,开始重写

人为触发重写

1
BgReWriteAOF

为什么要重写

当我们对一个 Key 反复操作的时候

1
2
3
4
5
6
7
set name 面向加薪学习
delete name
set name Go语言极简一本通
delete name
set name Go语言微服务架构师核心22讲
delete name
set name 从0到Go语言微服务架构师

我们看到上面对 name 的反复操作,那么 redis 重写以后会保留最后一条,这样,更好的提高性能。

AOF 优点

  1. 数据不易丢失(每秒执行 1 次)
  2. 自动重写机制
  3. 易读易恢复

AOF 缺点

  1. AOF 文件恢复文件慢
  2. AOF 持久化效率低

RDB vs AOF 如何选择

同时开启

Redis 会先加载 AOF 文件恢复数据,因为 AOF 比 RDB 更完整,如果错误的操作记录写入了 AOF,会导出数据恢复失败,所以 RDB 作为备份数据,如果为了考虑性能,可以只在 Slave 上开启 RDB,因为 RDB 可能 15 分钟备份一次,为了避免 AOF Rewrite 的 I/O 操作以及阻塞,可以在 Redis 集群中不开启 AOF,靠集群的备份机制来保证可用性,在启动时选取最新的 RDB 文件,如果集群全部宕机崩溃,会丢失 15 分钟数据。

混合模式

默认开启的

1
aof-use-rdb-preamble true

Redis 在启动时,加载 AOF 文件,但是加载速度慢。但是,RDB 数据又不完整,所以加载 AOF。

开启后,AOF 在重写时会直接读取 RDB 的内容。

执行 bgrewriteaof 以后,运行过程:

  1. 子进程把内存中的数据以 RDB 的方式写入到 AOF 文件中。
  2. 把重写缓冲区中的增量命令以 AOF 形式写入到文件。
  3. 将含有 RDB 个数和 AOF 个数的 AOF 数据覆盖旧的 AOF 文件。

恢复数据

  1. AOF 文件开头是 RDB 格式,先加载 RDB 的内容,再加载其他的 aof 的内容。
  2. AOF 文件开头不是 RDB 格式,直接加载 AOF 文件。

优点:快速备份+避免大量数据丢失

缺点:RDB 是压缩模式,AOF 在读取时可读性较差

建议使用 混合模式

Redis 优化方案

  1. 独立部署,redis 的 rdb 和 aof 文件生成过程,除了对硬盘和内存造成压力,它是 cpu 密集型操作。不要和其他应用部署(MySQL 或 Kafka)到一起。MySQL 或 Kafka 也是要对磁盘造成压力。硬盘建议使用 SSD 硬盘。缓存禁用持久化。因为缓存失效后可以重新从数据库里面再拉出来,所以没必要持久化。

  2. 主从模式,从节点持久化。因为从节点都是只读节点,是不会有写入功能的。从节点数据是来自主节点的,每次复制,主节点需要一次 BgSave,磁盘要交互一次,然后生成 RDB 文件,然后再把 RDB 文件发给从节点。这就完成一次复制的过程。

  3. 优化 Fork 处理,如果从节点频繁和主节点进行数据复制,那么主节点一直执行 BgSave,也是亚历山大。主从模式可以把主节点持久化关闭,让从节点开启 RDB 的文件备份。甚至可以把 AOF 关闭,因为主从已经是高可用了,但是如果说 15 分钟内有一个节点宕机,那么其他节点的数据还存在,这个主从模式和单节点模式是不同的,因为单节点一旦挂了,什么数据就都没了。如果可以关闭 AOF,那么就是又节省了一大部分系统开销。还可以降低 AOF 重写频率。


如何学以致用,在哪些场景中应用Redis

《Go语言+Redis实战课》

Go语言+Redis实战课-课程大纲 《Go语言+Redis实战课》课程+优惠券合并照片
添加微信 公众号更多内容
wechat gzh