Redis持久化机制
为什么要持久化
如果Redis再次访问时,发现Redis的数据是空的,就会形成缓存穿透。更重要的是,因为Redis的数据是空的,所以客户端想要访问的key都没有,就会造成大量的请求就会瞬间打到数据库上,造成缓存雪崩(少量的key是穿透,大量的key是雪崩)。
这个时候,数据库可能就挂掉。而又无法保证redis不宕机,所以需要当redis宕机后,迅速将里里面的内容恢复出来。因此需要做一个持久化。持久化是为了恢复数据用的,而不是存储数据用的。【相关推荐:Redis视频教程】
RDB
RDB(Redis DataBase),是Redis默认的存储方式,RDB方式是通过快照(snapshotting)完成的。
触发快照的方式
符合自定义配置的快照规则
save 900 1 # 表示15分钟(900秒钟)内至少1个键被更改则进行快照。
save 300 10 # 表示5分钟(300秒)内至少10个键被更改则进行快照。
save 60 10000 # 表示1分钟内至少10000个键被更改则进行快
N秒内数据集至少有M个改动”这一条件被满足时,自动保存一次数据集。
执行save或者bgsave命令
执行命令save或bgsave可以生成dump.rdb文件,每次命令执行都会将所有redis内存快照到一个新的rdb文件里,并覆盖原有rdb快照文件。
save与bgsave对比:
命令 | save | bgsave |
---|---|---|
IO类型 | 同步 | 异步 |
是否阻塞redis其它命令 | 是 | 否(在生成子进程执行调用fork函数时会有短暂阻塞) |
复杂度 | O(n) | O(n) |
优点 | 不会消耗额外内存 | 不阻塞客户端命令 |
缺点 | 阻塞客户端命令 | 需要fork子进程,消耗内存 |
配置自动生成rdb文件后台使用的是bgsave方式。
执行flushall命令
flushall
清空Redis之前,保存当前Redis快照
执行主从复制操作 (第一次)
第一次主从复制时需要生成rdb文件,会保存当前Redis快照
RDB执行流程
流程分析
- Redis父进程首先判断:当前是否在执行save或bgsave/bgrewriteaof(aof文件重写命令)的子进程,如果在执行则bgsave命令直接返回。
- 父进程执行fork(调用操作系统函数复制主进程)操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令。
- 父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令。
- 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换。(RDB始终完整)
- 子进程发送信号给父进程表示完成,父进程更新统计信息。
- 父进程fork子进程后,继续工作。
RDB文件结构
- 1、头部5字节固定为“REDIS”字符串
- 2、4字节“RDB”版本号(不是Redis版本号),当前为9,填充后为0009
- 3、辅助字段,以key-value的形式
- 4、存储数据库号码
- 5、字典大小
- 6、过期key
- 7、主要数据,以key-value的形式存储
- 8、结束标志
- 9、校验和,就是看文件是否损坏,或者是否被修改
RDB的优缺点
优点
RDB是二进制压缩文件,占用空间小,便于传输(传给slaver)
主进程fork子进程,可以最大化Redis性能,主进程不能太大,复制过程中主进程阻塞
缺点
- 不保证数据完整性,会丢失最后一次快照以后更改的所有数据
AOF
AOF(append only file)是Redis的另一种持久化方式。Redis默认情况下是不开启的。开启AOF持久化后,Redis将所有对数据库进行过写入的命令(及其参数)(RESP)记录到AOF文件,以此达到记录数据库状态的目的,
这样当Redis重启后只要按顺序回放这些命令就会恢复到原始状态了。AOF会记录过程,RDB只管结果
AOF持久化实现
配置 redis.conf
# 可以通过修改redis.conf配置文件中的appendonly参数开启 appendonly yes # AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的。 dir ./ # 默认的文件名是appendonly.aof,可以通过appendfilename参数修改 appendfilename appendonly.aof
AOF原理
AOF文件中存储的是redis的命令,同步命令到 AOF 文件的整个过程可以分为三个阶段:
命令传播:Redis将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF 程序中。
缓存追加:AOF程序根据接收到的命令数据,将命令转换为网络通讯协议的格式,然后将协议内容追加 到服务器的AOF缓存中。
文件写入和保存:AOF 缓存中的内容被写入到 AOF 文件末尾,如果设定的 AOF 保存条件被满足的话, fsync函数或者fdatasync 函数会被调用,将写入的内容真正地保存到磁盘中。
命令传播
当一个Redis客户端需要执行命令时,它通过网络连接,将协议文本发送给Redis服务器。服务器在接到客户端的请求之后,它会根据协议文本的内容,选择适当的命令函数,并将各个参数从字符串文本转换为Redis 字符串对象(StringObject)。每当命令函数成功执行之后,命令参数都会被传播到AOF程序。
缓存追加
当命令被传播到AOF程序之后,程序会根据命令以及命令的参数,将命令从字符串对象转换回原来的协议文本。协议文本生成之后,它会被追加到redis.h/redisServer结构的aof_buf 末尾。
redisServer结构维持着Redis服务器的状态,aof_buf域则保存着所有等待写入到AOF 文件的协议文本(RESP)
文件写入和保存
每当服务器常规任务函数被执行、或者事件处理器被执行时,aof.c/flushAppendOnlyFile 函数都会被调用,这个函数执行以下两个工作:
WRITE:根据条件将aof_buf中的缓存写入到AOF文件。
SAVE:根据条件调用fsync或 fdatasync函数将AOF文件保存到磁盘中。
AOF保存模式
Redis 目前支持三种 AOF 保存模式,它们分别是:
AOF_FSYNC_NO :不保存。
AOF_FSYNC_EVERYSEC :每一秒钟保存一次。(默认)
AOF_FSYNC_ALWAYS :每执行一个命令保存一次。(不推荐)
AOF_FSYNC_NO
从不fsync,将数据交给操作系统来处理。更快,也更不安全的选择。
SAVE只会在以下任意一种情况中被执行:
Redis被关闭
AOF功能被关闭
系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行)
这三种情况下的SAVE操作都会引起Redis主进程阻塞。
AOF_FSYNC_EVERYSEC
SAVE原则上每隔一秒钟就会执行一次,因为SAVE操作是由后台子线程(fork)调用的, 所以它不会引起服务器主进程阻塞,并且在故障时只会丢失1秒钟的数据。
AOF_FSYNC_ALWAYS
每次执行完一个命令之后,WRITE和SAVE都会被执行。每次有新命令追加到AOF文件时就执行一次fsync,非常慢,也非常安全。
因为SAVE是由Redis主进程执行的,所以在SAVE执行期间,主进程会被阻塞,不能接受命令请求。
AOF保存模式对性能和安全性的影响
三种模式的比较
AOF重写
AOF记录数据的变化过程,越来越大,需要重写“瘦身”
Redis可以在AOF体积变得过大时,自动地在后台(Fork子进程)对AOF进行重写。
重写后的新AOF文件包含了恢复当前数据集所需的最小命令集合。
所谓的“重写”其实是一个有歧义的词语,实际上,AOF重写并不需要对原有的AOF文件进行任何写入和读取,它针对的是数据库中键的当前值。
举例说明
set s1 11 set s1 22 set s1 33 lpush list1 1 2 3 lpush list1 4 5 6
AOF重写后
set s1 33 lpush list1 1 2 3 4 5 6
Redis不希望AOF重写造成服务器无法处理请求,所以Redis决定将AOF重写程序放到(后台)子进程里执行,
1、子进程进行AOF重写期间,主进程可以继续处理命令请求。
2、子进程带有主进程的数据副本,使用子进程而不是线程,可以在避免锁的情况下,保证数据的安全性。
不过,使用子进程也有一个问题需要解决:因为子进程在进行AOF重写期间,主进程还需要继续处理命令,而新的命令可能对现有的数据进行修改,这会让当前数据库的数据和重写后的AOF文件中的数据不一致。
为了解决这个问题,Redis增加了一个AOF重写缓存,这个缓存在fork出子进程之后开始启用,Redis主进程在接到新的写命令之后,除了会将这个写命令的协议内容追加到现有的AOF文件之外,还会追加到这个缓存中。
重写过程分析
Redis在创建新AOF文件的过程中,会继续将命令追加到现有的AOF文件里面,即使重写过程中发生停机,现有的AOF文件也不会丢失。而一旦新AOF文件创建完毕,Redis就会从旧AOF文件切换到新AOF文件,并开始对新AOF文件进行追加操作。
当子进程在执行AOF重写时,主进程需要执行以下三个工作:
- 处理命令请求。
- 将写命令追加到现有的AOF文件中。
- 将写命令追加到AOF重写缓存中
。这样一来可以保证:现有的AOF功能会继续执行,即使在AOF重写期间发生停机,也不会有任何数据丢失。所有对数据库进行修改的命令都会被记录到AOF重写缓存中。
当子进程完成AOF重写之后,它会向父进程发送一个完成信号,父进程在接到完成信号之后,会调用一个信号处理函数,并完成以下工作:
- 将AOF重写缓存中的内容全部写入到新AOF文件中。
- 对新的AOF文件进行改名,覆盖原有的AOF文件。
Redis数据库里的+AOF重写过程中的命令------->新的AOF文件---->覆盖老的当步骤1执行完毕之后,现有AOF文件、新AOF文件和数据库三者的状态就完全一致了。
当步骤2执行完毕之后,程序就完成了新旧两个AOF文件的交替。这个信号处理函数执行完毕之后,主进程就可以继续像往常一样接受命令请求了
。在整个AOF后台重写过程中,只有最后的写入缓存和改名操作会造成主进程阻塞,在其他时候,AOF后台重写都不会对主进程造成阻塞,这将AOF重写对性能造成的影响降到了最低。
AOF重写触发方式
- 1、配置触发
#表示当前aof文件大小超过上一次aof文件大小的百分之多少的时候会进行重写。如果之前没有重写过,以启动时aof文件大小为准 auto-aof-rewrite-percentage 100 #限制允许重写最小aof文件大小,也就是文件大小小于64mb的时候,不需要进行优化 auto-aof-rewrite-min-size 64mb
- 2、执行bgrewriteaof命令
127.0.0.1:6379>bgrewriteaof‘ Backgroundappendonlyfilerewritingstarted
AOF重写总结
混合持久化
RDB和AOF各有优缺点,Redis 4.0开始支持rdb和aof的混合持久化。
如果把混合持久化打开,aofrewrite的时候就直接把rdb的内容写到aof文件开头。
RDB的头+AOF的身体---->appendonly.aof
开启混合持久化
aof-use-rdb-preambleyes
AOF文件的载入与数据还原
如果开启了混合持久化,AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理,并且将RDB快照内容和增量的AOF修改内存数据的命令存在一起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换。
于是在Redis重启的时候,可以先加载RDB的内容,然后再重放增量AOF日志就可以完全替代之前的AOF全量文件重放,因此重启效率大幅得到提升。
- 1、创建一个不带网络连接的伪客户端(fake client)
因为Redis的命令只能在客户端上下文中执行,而载入AOF文件时所使用的命令直接来源于AOF文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端来执行AOF文件保存的写命令,伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样
2、从AOF文件中分析并读取出一条写命令
3、使用伪客户端执行被读出的写命令
4、一直执行步骤2和步骤3,直到AOF文件中的所有写命令都被处理完毕为止
Redis数据备份策略
1.写crontab定时调度脚本,每小时都copy一份rdb或aof的备份到一个目录中去,仅仅保留最近48小时的备份
2.每天都保留一份当日的数据备份到一个目录中去,可以保留最近1个月的备份
3.每次copy备份的时候,都把太旧的备份给删了
4.每天晚上将当前机器上的备份复制一份到其他机器上,以防机器损坏
RDB和AOF对比
1、RDB存某个时刻的数据快照,采用二进制压缩存储,AOF存操作命令,采用文本存储(混合)
2、RDB性能高、AOF性能较低
3、RDB在配置触发状态会丢失最后一次快照以后更改的所有数据,AOF设置为每秒保存一次,则最多丢2秒的数据
4、Redis以主服务器模式运行,RDB不会保存过期键值对数据,Redis以从服务器模式运行,RDB会保存过期键值对,当主服务器向从服务器同步时,再清空过期键值对。AOF写入文件时,对过期的key会追加一条del命令,当执行AOF重写时,会忽略过期key和del命令。
更多编程相关知识,请访问:编程视频!!
以上就是详细解析Redis中的持久化机制的详细内容,转载自php中文网
发表评论 取消回复