目录
一、RDB
RDB触发保存的两种方式
优劣势总结
二、AOF
AOF持久化流程:
1、开启AOP
2、异常恢复
3、AOF的同步频率设置
4、ReWrite压缩
5、优劣势总结
Redis 4.0 混合持久化
redis是内存数据库,所有的数据都会默认存在内存中,如果不进行持久化操作,当出现断电等问题,数据都不会保存
是指在时间间隔内将内存中的数据集快照写入磁盘。Redis会单独创建一个子进程来进行持久化,会先将数据写入一个临时文件中,待持久化都结束了,再用这个临时文件替换上一次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能,如果需要进行大规模的数据恢复,且对于恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加高效,RDB的缺点是最后一次持久化后的数据可能会丢失。在redis启动的时候会自动将rdb文件的数据读到内存中。
1、自动触发
进入配置文件中将sava注释打开
首先 在默认配置下我们很难演示这个过程,所以需要对它进行修改
新增了一条配置,在10秒内执行两次即可实现写入操作,修改完配置之后重启redis进行测试
现在data目录下是没有文件的
在10秒内输入三条命令
会将数据写入dump.rdb文件中
每次触发保存只会保存两条,根据save的条件进行保存。
-----------------------------------------------------------------------------------------------------------------------------
2、手动触发
①、save
该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。
显然该命令对于内存比较大的实例会造成长时间阻塞,这是致命的缺陷,为了解决此问题,Redis提供了第二种方式。
②、bgsave
执行bgsave命令的时候,redis会自己fork出一条子进程,由这条子进程去执行,这样就不会影响到客户端对于redis 的正常操作 有意思的是,我们都知道,进程与进程之间的内存不是共享的,那么子进程是如何获取到主进程的内存数据呢?
真像是在主进程fork子进程的同时,会把自己内存中的数据同时复制一份给子进程,这样就相当于子进程可以读取到主进程的数据了,然后子进程就可以愉快的进行io操作了.(将内存中的数据写入磁盘中).有的小伙伴可能会问,那么这个复制内存的操作是立即执行的吗,其实并不是的,正常情况下redis的服务大部分都是读操作,在fork子进程的时候,子进程其实并没有直接复制一份主进程的数据,而是给他分配了一个虚拟的内存地址,指向了父进程的内存地址,如果在fork期间客户端发起了新的操作,父进程的内存数据发生改变的时候才会进行复制内存的操作,此时子进程使用副本内存进行写入rdb文件,而主进程使用原始内存继续进行写入操作。为了性能,基本上 Redis 内部所有的RDB操作都是采用 bgsave 命令,也就是自动提交。
写时复制:
kernel把父进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向父进程。当父子进程都只读内存时,相安无事。当其中某个进程写内存时,CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入kernel的一个中断例程。中断例程中,kernel就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份。rdb就是采用了这种机制,避免了双倍内存占用的现象
③、flushall
执行执行 flushall 命令,也会产生dump.rdb文件,但里面是空的,没有意义。
save于bgsave对比
命令 |
save |
bgsave |
IO类型 |
同步 |
异步 |
是否阻塞redis其它命令 |
是 |
否(在生成子进程执行调用fork函数时会有短暂阻塞) |
复杂度 |
O(n) |
O(n) |
优点 |
不会消耗额外内存 |
不阻塞客户端命令 |
缺点 |
阻塞客户端命令 |
需要fork子进程,消耗内存 |
优势
劣势
以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有指令记录下(读操作不记录),只需追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从头到尾执行一次以完成数据恢复工作。
在Redis中AOF是默认不开启的需要我们手动开启
搜索appendonly找到该位置
将no改成yes
重启redis即可看到aof文件
要点: 当AOF和RDB同时开启的时候,系统默认读取AOF的数据(数据不会丢失),所以之前如果用的是rdb持久化数据再转成AOF,那么就读取不到之前的数据。
打开了之后每执行一指令就会自动持久化
当AOF文件出现异常的时候,redis的客户端时不能正常连接的,这时候需要我们修复文件
现在先模拟一下异常情况:
①、先存入几条数据
②、打开编辑aof文件
在里面加入自定义字符串让文件出错
然后重启一下redis,重新启动redis客户端,会发现已经不能连接了
③、解决方案
容器启动不起来,我们不能进入容器中的/usr/local/bin/找到redis-check-aof。但是有其他的方法
输入 :
docker inspect 容器id
Ⅰ、先找到备份的aop叫什么名字(以防不是默认文件名的情况)
找到了data,就是我们存放aof文件的位置,而source就是宿主机数据卷的位置,复制一下source的路径进入其中
可以看到aof文件,这里我用的是默认名字
Ⅱ、找到修复文件工具(redis-check-aof)
我们知晓这个工具的具体名字 可以直接通过find命令找到
find / --name “文件名”
找到文件后直接执行
找到的文件名加上 --fix 后面跟着aof文件的路径执行即可
始终同步,每次Redis的写入都会立刻计入日志;性能较差但数据完整性较好。
每秒同步,每秒计入日志一次,如果宕机,本秒的数据可能会丢失
redis不主动进行同步,把同步时机交给操作系统
Aof采用文件追加方式,文件会越来越大,为了避免出现这种情况,新增了重写机制,当AOF文件大小超过所设定的阈值时,Redis就会启用AOF文件的内容压缩,只保留可以恢复的数据的最小指令集,也可以使用bgrewriteaof手动压缩
如何实现?
AOF文件持续增长时,会fork出一条新进程来将文件重写(也就是先写入临时文件,最后再rename),而父进程继续接受命令,现在的写操作命令都会被额外添加到一个aof_rewrite_buf_blocks缓冲中当子进程rewrite结束后,父进程收到子进程退出信号,把aof_rewrite_buf_blocks的缓冲添加到rewrite后的文件中,然后切换AOF的文件fd。
重写策略?
如上图所示,上图配置的大体意思为当AOF文件大小超过了64m并且超过了64m的100%(128m)就开始重写。
# auto-aof-rewrite-min-size 64mb //aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大
# auto-aof-rewrite-percentage 100 //aof文件自上一次重写后文件大小增长了100%则再次触发重写
appendfsync-on-rewrite
同时在执行bgrewriteaof操作和主进程写aof文件的操作,两者都会操作磁盘,而bgrewriteaof往往会涉及大量磁盘操作,这样就会造成主进程在写aof文件的时候出现阻塞的情形,现在no-appendfsync-on-rewrite参数出场了。如果该参数设置为no,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题。如果设置为yes呢?这就相当于将appendfsync设置为no,这说明并没有执行磁盘操作,只是写入了缓冲区,因此这样并不会造成阻塞(因为没有竞争磁盘),但是如果这个时候redis挂掉,就会丢失数据。丢失多少数据呢?在linux的操作系统的默认设置下,最多会丢失30s的数据
优势
劣势
重启 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 全量文件重放,因此重启效率大幅得到提升。
混合持久化AOF文件结构如下