【Redis】持久化
Redis是一个「内存数据库」,把数据存储在内存中
//把数据存储在硬盘上是持久的,存储在内存上是不持久的
因此Redis的数据是不持久的
所以Redis相比于MySQL这样的关系型数据库,最明显的优势是「效率快」
插入一个新数据时,Redis会把数据同时写入到内存和硬盘中,查询数据时直接从内存读取,硬盘中的数据只在Redis重启时,用来恢复内存中的数据,但代价是消耗了更多的空间,同一份数据中存储了两遍
【Redis实现持久化的策略】
【RDB——Redis DataBase】
定期备份
RDB定期的把Redis内存中的所有数据,都写入硬盘中,生成一个「快照」
Redis给内存中当前存储的数据拍个照片,生成一个文件,存储在硬盘中,后续Redis一旦重启了(内存中数据就没了),就可以根据刚才的快照,把内存中的数据恢复回来
//类似于游戏中的存档和读档
通过正常流程重新启动Redis,则Redis会在退出时自动触发执行RDB,但若异常重启(kill命令等),则Redis会来不及执行RDB,内存中未保存到快照中的文件会丢失
而「定期」又有两种方式
1.手动触发
程序员通过Redis客户端,执行特定的命令,来触发快照的生成
命令:「save」或「bgsave」
「save」
执行save时,Redis会全力以赴的进行“快照生成”的操作,此时就会阻塞Redis的其他客户端的命令,可能导致严重发后果,一般不建议使用save
「bgsave」
bg => background(后面)
Redis在背后悄悄进行快照生成操作,不会影响Redis服务器处理其他客户端的请求和命令
2.自动触发
在Redis配置文件中,设置一下,让Redis每隔多长时间/每产生多少次修改,就触发快照的生成
【RDB快照执行流程】
1.判断当前是否存在其他执行bgsave的子进程,若存在,则把所有其他的bgsave返回
2.若无,则通过folk这样的系统调用,创建一个子进程
//folk是Linux系统提供的一个创建子进程的api,其方式简单粗暴,直接把当前进程(父进程)复制一份,作为子进程
复制完成后,父子进程是两个独立的进程,各自执行各自的任务
3.子进程负责进行「写文件,生成快照」的过程,父进程继续接收客户端的请求,继续正常提供服务
4.子进程完成整体的持久化过程后,会通知父进程,干完了后,父进程会更新统计信息,子进程就可以结束销毁了
RGB最大的问题是,不能实时地持久化保存数据,在两次生成快照之间,实时的数据可能会丢失,这样的问题可以通过AOF来解决
【AOF——Append Only File】
实时备份
会把用户的每个操作,都记录到文件中,当Redis重启时,会读取这个aof的内容,用来恢复数据
//当开启AOF时,RDB就不生效了,启动时就不再读取RDB文件中的内容(因此AOF是默认关闭的)
【AOF重写执行流程】
Redis存在这样的「重写机制」,可以针对AOF进行整理操作,可以剔除冗余操作,并且合并一些操作,达到给AOF“瘦身”的效果
子进程写AOF文件时,父进程仍然在不断接收客户端的新请求,不断把这些请求产生的AOF数据先写入到aof_buf缓冲区,再刷新到原有的AOF文件中
在父进程创建子进程时,子进程会继承当前父进程的内存状态(因此fork之后新来的请求对内存造成的修改,是子进程不知道的)
因此,父进程有一个aof_rewite_buf缓冲区,专门放fork后收到的数据
子进程这边把AOF数据写完后,会通过信号来通知父进程,父进程收到信号后,把aof_rewite_buf缓冲区中的内容也写入到新AOF文件中
此时,就可以用新AOF文件来替代旧AOF文件了
【混合持久化】
Redis拥有「混合持久化」的配置选项,其结合了RDB和AOF的特点
//上图选项为yes则为开启混合持久化,no则为关闭
其按照AOF的方式,将每一个请求/操作,都记入文件
在触发AOF重写后,会把当前内存状态按照RDB的二进制格式写入到新AOF文件中
后续再进行的操作,仍按照AOF文本的方式追加到文件后面
【补充】
在执行bgrewriteaof时,若当前Redis正在进行AOF重写,则:
此时不会再次执行AOF重写,直接返回
在执行bgrewriteaof时,若当前Redis正在生成RDB文件的快照,则:
AOF重写操作会等待,一直等待到RDB快照生成完毕后,再进行执行AOF重写
Redis上同时存在AOF文件与RDB快照时,以AOF为主
原因是AOF中包含的数据要比RDB多