1. 什么是Redis持久化
- 很多时候我们需要持久化数据,也就是将内存中的数据写⼊到硬盘⾥⾯,⼤部分原因是为了之后重⽤数据(⽐如重启机器、机器故障之后恢复数据),或者是为了防⽌系统故障⽽将数据备份到⼀个远程位置。
- Redis会单独fork一个与当前进程一模一样的子进程来进行持久化,这个子进程的所有数据(变量、环境变量、程序程序计数器等)都和原进程一模一样,会先将数据写入到一个临时文件中,待持久化结束了,再用这个临时文件替换上次持久化好的文件,整个过程中,主进程不进行任何的I/O操作,这就确保了极高的性能。
- redis提供两种持久化方式:
1.RDB(Redis DataBase)持久化:将Reids在内存中的数据库记录定时dump到磁盘上。
2.AOF(Append-Only File)持久化:将Reids的操作日志以追加的方式写入文件。
2. RDB
Redis 可以通过创建快照来获得存储在内存⾥⾯的数据在某个时间点上的副本。
- Redis 创建快照之后,可以对快照进⾏备份,可以将快照复制到其他服务器从⽽创建具有相同数据的服务器副本(Redis 主从结构,主要⽤来提⾼ Redis 性能),还可以将快照留在原地以便重启服务器的时候使⽤。
- 快照持久化是 Redis 默认采⽤的持久化⽅式,在 Redis.conf 配置⽂件中默认有此下配置:
save 900 1 #在900秒(15分钟)之后,如果⾄少有1个key发⽣变化,Redis就会⾃动触发BGSAVE命令创建快照。 save 300 10 #在300秒(5分钟)之后,如果⾄少有10个key发⽣变化,Redis就会⾃动触发BGSAVE命令创建快照。 save 60 10000 #在60秒(1分钟)之后,如果⾄少有10000个key发⽣变化,Redis就会⾃动触发BGSAVE命令创建快照。
- 还可以手动执行命令生成RDB快照,进入redis客户端执行命令save或bgsave可以生成dump.rdb文件,每次命令执行都会将所有redis内存快照到一个新的rdb文件里,并覆盖原有rdb快照文件。
2.1. bgsave写时复制的(COW)机制
- Redis借助了linux系统的写时复制(Copy-On-Write)技术,在生成快照的同时,仍然可以接收命令处理数据。
- 简单来说,bgsave线程是由主线程fork生成的子线程,可以共享主线程所有的内存数据。
- bgsave线程运行后,开始读取主线程的内存数据,也就是redis的内存数据,将内存数据写入到dump.rdb文件中。
- 此时,如果主线程处理的命令都是读操作,则bgsave线程不受影响。
- 如果主线程处理了写操作,则会对该命令操作的数据复制一份,生成副本,bgsave线程会把这个副本写入到dump.rdb文件中,而在这个过程中,主线程仍可执行命令。
2.2. bgsave与save对比
differences | save | bgsave |
I/O类型 | 同步 | 异步 |
是否阻塞其他命令 | 是 | 否(但fork会短暂阻塞) |
复杂度 | O(n) | O(n) |
优点 | 不会额外消耗内存 | 不阻塞操作 |
缺点 | 阻塞操作 | 会消耗额外的内存 |
配置自动生成rdb文件后台使用的是bgsave方式。
2.3. RDB方式的优点
- dump.rdb文件是一个非常紧凑(compact)的二进制文件,它保存了Redis 在某个时间点上的数据集,非常适合用于进行备份和灾难恢复,所以当Redis服务崩溃恢复的时候,能很快的将文件数据恢复到内存之中。
- 生成RDB文件的时候,Redis主进程会fork一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
- RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
2.4. RDB方式的缺点
- RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作,如果不采用压缩算法(主进程 fork 出子进程,其实是共享一份真实的内存空间,但是为了能在记录快照的时候,也能让主线程处理写操作,采用的是 Copy-On-Write(写时复制)技术,只有需要修改的内存才会复制一份出来,所以内存膨胀到底有多大,看修改的比例有多大),频繁执行成本过高(影响性能);
- RDB在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改;
- RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题(版本不兼容);
3. AOF
- 为解决RDB方式丢失数据的问题,从1.1版本开始,redis增加了一种更加可靠的方式:AOF持久化方式。
- 以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录),追加到appendonly.aof文件中(先写入os cache,然后通过fsync刷盘), 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
- 可以通过修改配置文件来使用AOF:
# appendonly yes
- Redis重启的时候,会重放appendonly.aof中的命令恢复数据。
3.1. AOF持久化过程
- 客户端的请求写命令会被append追加到AOF缓冲区内;
- AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中;
- AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;
- Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的;
3.2. AOF刷盘策略
1.AOF可以配置三种刷盘策略:
策略 | 说明 |
appendfsync always | 每次有数据修改发⽣时都会写⼊AOF⽂件,这样会严重降低Redis的速度 |
appendfsync everysec | 每秒钟同步⼀次,显式地将多个写命令同步到硬盘 |
appendfsync no | 让操作系统决定何时进⾏同步 |
2.为了兼顾数据和写⼊性能,⽤户可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步⼀次AOF ⽂件,Redis 性能⼏乎没受到任何影响。⽽且这样即使出现系统崩溃,⽤户最多只会丢失⼀秒之内产⽣的数据。当硬盘忙于执⾏写⼊操作的时候,Redis 还会优雅的放慢⾃⼰的速度以便适应硬盘的最⼤写⼊速度。
3.3. AOF重写
- 因为appendonly.aof文件中存储的是执行命令,所以会产生很多没用的命令,因此,redis会定期根据最新的内存数据生成新的aof文件。
- AOF 重写可以产⽣⼀个新的 AOF ⽂件,这个新的 AOF ⽂件和原有的 AOF ⽂件所保存的数据库状态⼀样,但体积更⼩。
- AOF 重写是⼀个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序⽆须对现有AOF ⽂件进⾏任何读⼊、分析或者写⼊操作。
- 在执⾏ BGREWRITEAOF 命令时,Redis 服务器会维护⼀个 AOF 重写缓冲区,该缓冲区会在⼦进程创建新 AOF ⽂件期间,记录服务器执⾏的所有写命令。当⼦进程完成创建新 AOF ⽂件的⼯作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF ⽂件的末尾,使得新旧两个 AOF ⽂件所保存的数据库状态⼀致。最后,服务器⽤新的 AOF ⽂件替换旧的 AOF ⽂件,以此来完成 AOF ⽂件重写操作。
- 如下两个配置可以控制aop文件重写的频率:
# auto‐aof‐rewrite‐min‐size 64mb: -- aof文件至少达到了64m才会触发重写 # auto‐aof‐rewrite‐percentage 100: -- 距离上次重写增长了100%才会再次触发重写
- 注意,AOF重写时,Redis会fork出一个子进程去做(与bgsave命令类似),不会对Redis正常命令处理有太多影响.
3.4. AOF方式的优点
- 备份机制更稳健,丢失数据概率更低。
- 可读的日志文本,通过操作AOF文件,可以处理误操作。
3.5. AOF方式的缺点
- 比起RDB占用更多的磁盘空间。
- 恢复备份速度要慢。
- 每次读写都同步的话,有一定的性能压力。
4. RDB VS AOF
differences | RDB | AOF |
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 容易丢数据 | 根据策略决定 |
5. Redis 4.0 混合持久化
- 重启 Redis 时,我们很少使用 RDB来恢复内存状态,因为会丢失大量数据。
- 我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 RDB来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。
- Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。
- 通过如下配置可以开启混合持久化(必须先开启aof):
# aof‐use‐rdb‐preamble yes
- 如果开启了混合持久化,AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理,并且将RDB快照内容和增量的AOF修改内存数据的命令存在一起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换。
- 于是在 Redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量 AOF 日志就可以完全替代之前的AOF 全量文件重放,因此重启效率大幅得到提升。