Redis 持久性
认识持久化
在MySQL的事务中,有四个核心的属性:
1.原子性
2.一致性
3.持久化
4.隔离性。
其中这个持久化就是指将数据存储到硬盘上,进程在重启 / 主机重启之后,数据依然存在。
然而Redis是一个内存数据库,但是它也有持久化的策略。
RDB:Redis DataBase 。主要是采取一个定期备份的策略。
AOF:Append Only File。主要采取实时备份的策略。
RDB机制
RDB的触发时机
RDB会定期把Redis的所有数据都写入到硬盘中,生成一个 “快照”
如果Redis一旦重启了,就可以根据之前的快照把数据恢复出来。
定期触发具体来说又有两种方式:
a.手动触发 :
程序员通过Redis客户端,执行特定命令的方式,来触发快照的生成。
这里又有两个命令:
1.save命令:
执行save命令,Redis就会执行快照生成的操作,此时就会阻塞Redis的其他客户端的命令,导致类似于keys * 的后果。一般不建议使用save。
2.bgsave命令:
bg => background(后面) ,bgsave不会影响Redis服务器处理其他客户端的请求和命令。
此处Redis是用多进程的方式来完成并发编程,也就是bgsave的实现。
b.自动触发
在Redis的配置文件中,设置Redis每隔多少时间 / 每产生多少次修改 就触发。
RDB bgsave的执行流程
大致执行流程顺序 :
1.先判定当前是否有其他的子进程正在执行bgsave,如果有,那么直接把bgsave返回。
2.如果没有其他正在工作的子进程,那么就通过系统调用fork来创建一个子进程。
3.子进程负责进行写文件,生成快照的过程中,父进程正常接收处理客户端的请求和命令。
4.子进程完成持久化的操作后,就会通知父进程(以信号的方式),此时父进程就会更新一些统计信息,然后子进程就可以结束销毁了。
RDB的镜像文件
Redis生成的rdb文件,是存放在Redis的工作目录中的,这一点在配置文件中可以设置。
Redis的配置文件目录:
ls /etc/redis/
在配置文件中
上面这个就是rdb文件的默认名字。
这个目录就是 rdb文件 的存放目录,也就是Redis的工作目录。
进去之后,确实有一个 dump.rdb文件。
这个文件不要乱改,不然文件格式出错的话,就会导致Redis服务器无法启动或者其他的情况。
另外Redis还提供了 rdb文件的检查工具,先进入
cd /usr/bin/
然后
ll redis*
其中,这个redis-check-rdb就是检查工具了。
关于rdb文件
rdb的持久化操作是可以触发多次的,当执行生成rdb镜像的时候,就会把要生成的快照数据存放到一个临时.rdb文件中,然后再把原来的dump.rdb文件删除掉,然后将临时文件改名为durm.rdb。
RDB的演示效果
我们知道RDB的触发方式有两种:
1.手动触发(命令 save,bgsave)
2.自动触发(配置文件进行设置)
关于自动触发,我们在配置文件中可以找到如下:
上面是对save的描述,大致意思就是
比如 save 900 1表示的就是每900s,执行了一次修改,那么就会进行一次触发,
save 300 10 表示的是每300s内,执行了10次修改,就触发一次RDB。
这是关于自动触发的,我们可以修改这些数值,但是要有一个基本的原则:
生成一次RDB快照的成本是比较高的, 不能让这个操作过于频繁。
在save 60 10000可以知道,两次生成RDB快照的最短间隔是60s,如果中间60s如果Redis服务挂掉了(机器断电,设备故障),那么中间的数据就丢失了,而AOF就是解决这个问题的有效方案。
再来说自动触发,这里推荐使用 bgsave命令。
先看dump.rdb文件,虽然它是二进制的方式存储的
vim dump.rdb
我们在Redis客户端插入一些数据并保存
此时重新打开dump.rdb文件,就会发现不一样了。
仔细看,多了key1 key2这样的字符。
重启服务器之后,发现之前的key都还在。
停止redis-server
service redis-server stop
然后再启动
service redis-server start
这里发现我的数据就被恢复上来了。
另外,我们之前也提到过,RDB不仅可以手动触发,还可以自动触发:
1.比如刚刚展示的配置文件,达到条件时就会自动触发。
2.通过shutdown命令(Redis的命令)关闭Redis服务器时,也会触发。或者Ubuntu下 service redis-server restart这样正常关闭的方式也会自动触发。
3.Redis进行主从复制的时候,主节点也会自动生成快照,然后把快照传输给从节点。
关于dump.rdb文件的替换,因为我们当前的数据量太少了,所以bgsave几乎是瞬间完成的,所以很难观察到替换的过程。
我们可以通过观察原dump.rdb的Inode编号的方式来判断是否发生了替换
使用stat命令即可观察
stat dump.rdb
执行一次bgsave之后
我们发现Inode变了,说明发生了替换。
注意:如果是save命令,那么是不会触发 子进程 & 文件替换的逻辑的,而是会覆盖式的修改。
当我们执行flushall命令时,也会清空rdb文件的。
自动生成快照的功能也是可以在配置文件中关闭的。
如果rdb文件坏了会咋样?
如果我们手动的把rdb文件改坏了,然后Redis服务又是非正常关闭的(kill杀掉了),那么此时产生的后果就是不可预期的。有可能Redis可以启动,但是数据可能会有问题,或者还有可能Redis直接启动不了了。
当Redis服务挂了后,我们可以看看Redis的日志,看看发生了什么
日志的目录
ll /var/log/redis/
这个位置也是可以在配置文件中修改的。
其中.log就是日志文件。
之前也说过,Redis提供了rdb的文件检测工具
执行
redis-check-rdb dump.rdb
我这里是没有问题的。
RDB小总结:
RDB最大的问题就是不能实时的持久化保存数据,两次快照生成期间,实时的数据可能会伴随着重启而丢失。
AOF机制
关于AOF的基本操作
AOF默认是关闭状态,可以在配置文件中修改。
当开启AOF时,RDB就失效了。
AOF类似MySQLDbinlog,会把用户的每个操作都记录到文件中,当Redis重新启动时,就会读取这个aof文件,用来恢复数据。
因为aof文件是以文本的方式存储的,相比于用二进制存储的rdb文件,aof文件的读取速度要慢一些。
我们可以在配置文件中查找关于AOF的选项
关于vim的查找
我们可以在普通模式下(按Esc进入),输入 / + 需要查找的文本。按 n 可以进入下一个匹配项。
示例:
这里发现默认是no,也就是关闭的,我们可以修改成yes。
下面是aof的默认文件名。
修改成yes后,我们重启Redis服务器
然后再看看aof文件
发现可以看到我们设置的key1 key2。
AOF缓冲区的刷新策略
AOF对Redis的性能影响大吗?
直接先说结论:影响不是很大。也取决于AOF的刷新硬盘的策略。主要原因有两点:
1.AOF机制并不是直接让工作线程把数据写入到硬盘的,而是在内存上有一个缓冲区,会先把数据写入到缓冲区中,当缓冲区中积累了一定的数据时,再统一写入到硬盘中。
2.硬盘上读写数据顺序读写的速度还是比随机访问的速度要快很多的(当然还是比内存慢很多),AOF是每次把新的操作写入到文件的末尾,属于顺序写入。
到这里我们注意到这个缓冲区是在内存中的,那么如果此时数据还没来得及刷入硬盘,进程就挂掉或者机器掉电了,那么数据还是会丢失的。
此时Redis就会给出一些选项,让程序员自行选择缓冲区的刷新策略。
刷新频率越高,数据的可靠性就越高,但是性能影响的也就越大。反之。
Redis提供了三种策略。
第一种是频率最高的,一有命令就会刷新。
第二种就是每秒刷新。
第三种就是完全把刷新交给系统,比如缓冲区满了,或者进程正常退出了。
在配置文件中
我们发现默认就是第二种。
AOF的重写机制
重写是什么?为什么要重写?
因为AOF的最终目的就是为了保存Redis最后在内存中的状态,那么Redis在中间是什么样的状态我们其实并不关心,我们只关心Redis最终是什么状态。于是以最终状态的视角来看,aof文件中可能就会存在一些冗余的操作,比如:
随着aof文件的体积不断增大,就会导致下次启动Redis的时间越来越长。所以Redis可以通过剔除这些冗余操作的方式,来使得 aof文件达到 “瘦身” 的效果。
那什么时候会重写呢?
这里也分为手动触发和自动触发:
手动触发就是调用bgrewriteaof命令。
关于AOF重写的流程:
这里跟RDB的bgsave类似,也是通过fork创建子进程的方式。
父进程仍然接收请求,而子进程负责针对aof文件进行重写。
子进程重写的时候, 不关心原来的aof文件中有啥,它只关心内存中最终的数据状态。
子进程只需要把当前内存中的数据获取出来(这里的数据已经是剔除掉冗余操作后的数据了),以AOF的格式写入到一个新的aof文件中,写入完成之后,再替换掉原来旧的aof文件。
不过在这里还有一个需要注意的是,在父进程fork出子进程之后,此时子进程就继承了当前父进程的内存状态,但是在子进程重写AOF的时候,父进程正常接收请求,并且在创建完子进程之后,就会再准备一个 aof_rewrite_buf缓冲区,此时收到的请求会在 aof_rewrite_buf上写一份,还会在原来的aof_buf上写,并且aof_buf依旧会遵循原来的刷新规则。等子进程重写完毕之后,就会以信号的方式通知父进程,父进程再把aof_rewrite_buf中的数据写入到新的aof文件中,然后再替换掉旧的aof文件,此时重写才会结束。
到这里有些人可能就会疑惑,为什么父进程要把数据写两份呢?为什么不直接把数据写到aof_rewrite_buf,然后再追加到新的aof文件中不就行了吗?
其实这里不写还不行,因为要考虑到极端情况:假设子进程还在重写的时候,进程就挂了或者机器掉电了,那么子进程内存中的数据就会丢失,新的aof文件的数据就不完整,如果此时父进程没有坚持写旧的aof文件,那么重启后就无法保证数据完整性了。
关于AOF重写的其他问题:
a.如果手动重写 也就是执行bgrewriteaof命令时,Redis已经进行重写了,那么会咋样呢?
那么此时Redis不会再进行重写了,会直接返回。因为重写也没什么意义。
b.如果在执行bgrewriteaof时,发现当前Redis在生成rdb快照,会咋样呢?
此时AOF重写操作就会等待,等待rdb快照生成完之后,再执行AOF重写。
持久化总结
AOF机制和RDB机制各有各自的优缺点,在Redis中,默认是采取混合持久化的方式的。
简单概括就是:
RDB机制效率高(还包括了服务器的启动速度,因为是二进制数据),但是不能实时持久化。
AOF可以实时持久化,性能相对于RDB较低。
可以在配置文件中找到
所谓混合持久化就是结合了AOF和RDB的特点。
开启AOF后,Redis会把每一个操作写入aof文件中,但是在触发重写之后,Redis就会按照当前的内存状态,把数据以RDB二进制的格式写入到新的aof文件中,后续再进行操作时,又仍是按照AOF文本的方式追加到文件后面。
并且,当aof文件和rdb文件同时存在的时候,Redis启动时就会优先读取aof文件。
原理到这里我们也都清楚,因为aof文件包换的数据比rdb文件的要更全。