持久化(Persistence):即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。
在Redis
中有RBD
和AOF
两种持久化方式,但是一般默认RDB
。
默认中Redis
中是先把内存数据库快照保存在名字为dump.rdb
的二进制文件中。
我们能通过save
来配置持久化策略。
save N M #让redis在“N秒内至少有M个改动”才会触发一次rdb持久化操作。
#例如:
save 100 60 #表示在100秒内有60个改动
当执行save
命令时,Redis
同步做快照操作,在快照执行过程中会阻塞所有来自客户端的请求。当redis
内存中的数据较多时,通过该命令将导致Redis
较长时间的不响应。所以不建议在生产环境上使用这个命令,而是推荐使用bgsave
命令。
Redis
借助了linux
系统的写时复制(Copy-On-Write)
技术,在生成快照的同时,仍然可以接收命令处理数据。简单来说,bgsave
线程是由主线程fork
生成的子线程,可以共享主线程所有的内存数据。bgsave
线程运行后,开始读取主线程的内存数据,也就是redis
的内存数据,将内存数据写入到dump.rdb
文件中。此时,如果主线程处理的命令都是读操作,则bgsave
线程不受影响。如果主线程处理了写操作,则会对该命令操作的数据复制一份,生成副本,bgsave
线程会把这个副本写入到dump.rdb
文件中,而在这个过程中,主线程仍可执行命令。
和save的对比:
save | bgsave | |
---|---|---|
IO类型 | 同步 | 异步 |
是否阻塞其他命令 | 是 | 否 |
复杂度 | O(n) | O(n) |
优点 | 不消耗额外内存 | 不阻塞操作 |
缺点 | 阻塞操作 | 消耗额外内存 |
RDB的优点:
dump.rdb
是二进制文件,当发生灾难级别的数据丢失,使用二进制文件则可以很容易的进行恢复。RDB的缺点:
Redis
默认不开启。AOF
采用日志
的形式来记录每个写操作,并追加到文件中。开启后,执行更改Redis
数据的命令时,就会把命令写入到AOF
文件中。
Redis
重启时会根据日志文件的内容把写指令从前到后执行一次以完成数据的恢复工作。
手动配置:
# appendonly yes
AOF可以配置三种刷盘策略:
appendfsync always:每次执行写命令都会刷盘,非常慢,也非常安全。
appendfsync everysec:每秒刷盘一次,兼顾性能和安全。
appendfsync no:将刷盘操作交给系统,很快,不安全。
随着时间的推移,AOF
备份文件会越来越大!这样不仅导致占据大量的磁盘空间,也会使一些移动复制
,加载分析
显得非常耗时!
为了减少文件增加,可以采用 压缩 的方式来进行一个文件内存的缩小。
如下两个配置可以控制aop文件重写的频率:
# auto‐aof‐rewrite‐min‐size 64mb: -- aof文件至少达到了64m才会触发重写
# auto‐aof‐rewrite‐percentage 100: -- 距离上次重写增长了100%才会再次触发重写
AOF也可以手动触发重写:bgrewriteof
注意,AOF重写redis会fork出一个子进程去做(与bgsave命令类似),不会对redis正常命令处理有太多影响
重写的流程:
fork
一个子进程出来进行AOF
重写,并不是对原文件进行重新整理,而是直接读取redis
服务内存中现有的键值对,然后用一条命令去代替每个键值对,写入到新的AOF
文件中fork
子进程这个过程中,服务端仍然可以对外提供服务,在子进程重写的这个时间段里面,,主进程的数据更新操作,会缓存到aof_rewrite_buf
中,也就是单独开辟一块缓存来存储重写期间收到的命令,当子进程重写完以后再把缓存中的数据追加到新的aof
文件。aof
文件中后,会把旧的aof
文件替换成新的aof
文件,此后所有的操作都会被写入新的aof
文件。rewrite
过程中出现故障,不会影响原来aof
文件的正常工作,只有当rewrite
完成后才会切换文件。因此这个rewrite
过程是比较可靠的。在aof_buf缓存到旧的aof文件中间其实还有一个子进程,将aof_buf缓存中的数据同步到aof文件中取。
问题1:数据都是实时持久化到磁盘吗?
虽然每次执行更改Redis数据库内容的操作时,AOF都会将命令记录在AOF文件中,但是事实上,由于操作系统的缓存机制,数据并没有真正地写入硬盘,而是进入了aof_buf缓存中。在默认情况下系统每30秒会执行一次同步操作。以便将aof_buf缓存中的内容真正地写入磁盘中。
在这30秒的过程中如果系统异常退出则会导致aof_buf缓存中的数据丢失。这个时候就需要Redis在写入AOF文件后主动要求系统将aof_buf缓存内容同步到磁盘中。在redis.conf中通过如下配置来设置同步机制。
fork()出一个子进程,通过子进程将aof_buf缓存中的数据同步到磁盘中:
问题2:为什么要AOF重写?
比如我有业务很简单,就来回delete set同一个key。就这个业务运行了10年,那么aof文件将记录无数个delete k1, set k1。其实都是重复的,但是我aof每次都追加,文件变成了1T大小。这时候Redis宕机了,要恢复,你想想1TB大小的aof文件去恢复,累死了。最主要的是1TB大小只记录了两个命令,所以压缩其实就是来处理这件事的。
命令 | RDB | AOF |
---|---|---|
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 容易丢失数据 | 根据策略决定 |
如果我们采用 RDB
持久化会丢失一段时间数据。如果我们采用 AOF
持久化,AOF
日志较大,重放比较慢。
Redis 4.0 为了解决这个问题,支持混合持久化。将 RDB
文件的内容和增量的 AOF
日志文件存在一起。
混合持久化同样也是通过 bgrewriteaof
完成的,不同的是当开启混合持久化时,fork
出的子进程先将共享的内存副本全量的以 RDB
方式写入 AOF
文件,然后在将重写缓冲区的增量命令以 AOF
方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB
格式和 AOF
格式的 AOF
文件替换旧的的 AOF
文件。简单的说:新的AOF
文件前半段是RDB
格式的全量数据后半段是AOF
格式的增量数据。
于是在 Redis
重启的时候,可以先加载 rdb
的内容,然后再重放增量 AOF
日志就可以完全替代之前的 AOF
全量文件重放,重启效率因此大幅得到提升。
参考文章链接: