Redis的持久化机制
Redis是一个基于内存的数据库,它的数据是存放在内存中,内存有个问题就是关闭服务或者断电会丢失。Redis的数据也支持写到硬盘中,这个过程就叫做持久化。
Redis提供如下两种持久化方式
RDB(Redis DataBase) :在指定的时间间隔内,定时的将 redis 存储的数据生成Snapshot快照并存储到磁盘等介质上;
AOF(Append Of File) :将 redis 执行过的所有写指令记录下来,在下次 redis 重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。
AOF+RDB : RDB 和 AOF 两种方式也可以同时使用,在这种情况下,如果 redis 重启的话,则会优先采用 AOF 方式来进行数据恢复,这是因为 AOF 方式的数据恢复完整度更高。
RDB持久化机制:
Redis Database Backup file(Redis数据备份文件),也被叫作Redis数据快照。简单的来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件(二进制文件),恢复数据。快照文件称为RDB文件。
我们知道 Redis 是单线程程序,这个线程要同时负责多个客户端套接字的并发读写操作和内存数据结构的逻辑读写。
在服务线上请求的同时,Redis 还需要进行内存快照,内存快照要求 Redis 必须进行文件 IO 操作,这意味着单线程同时在服务线上的请求还要进行文件 IO 操作,文件 IO 操作会严重拖垮服务器请求的性能。还有个重要的问题是为了不阻塞线上的业务,就需要边持久化边响应客户端请求。
SAVE的执行时机:
1.执行save命令:在执行持久化的过程同时去阻塞redis进程
2.执行bgsave命令:异步持久化,不会阻塞redis的使用(一般来说都是使用这个命令)
3.触发RDB条件时候
在 redis.conf 文件中会有相关的配置信息
save ,满足条件就将内存中的数据同步到硬盘中。
save 900 1
900秒内,如果至少有1个key被修改,则执行bgsave , 如果是save "" 则表示禁用RDB
实际操作
save:
可以通过redis服务端可以通过指定目录的形式读取该目录下的rdb文件
bgsave:
Redis 在持久化时会调用 glibc 的函数 fork 产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求。子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段。这时你可以将父子进程想像成一个连体婴儿,共享身体。这是 Linux 操作系统的机制,为了节约内存资源,所以尽可能让它们共享起来。在进程分离的一瞬间,内存的增长几乎没有明显变化。
如果当redis子线程持久化的数据正常被父线程再修改的时候怎么办?
在Redis中,当子线程(如bgsave子进程)进行持久化操作时,如果父线程(主进程)对数据进行了修改,Redis采用了一种称为COW(copy-on-write)的技术来处理这种情况。具体来说,当父线程尝试修改一块内存中的数据时,这块数据会被复制一份,生成一个数据副本。然后,子线程可以使用这个数据副本进行持久化操作,而不会影响到正在被修改的原始数据。
这种机制确保了即使在持久化过程中发生数据修改,子线程也能准确地将最新的数据状态写入到RDB文件中,从而保证数据的一致性和完整性。此外,由于COW技术涉及到的数据复制是临时的,它不会导致额外的性能开销,因为这些复制的数据最终会被原数据替换或删除
注意:
这里会有一个极端的情况,比如Redis内存大小是32个GB,在某一个时刻,这32GB的数据都被修改了,根据上面COW技术,那么就需要把当前数据在复制一份,需要内存大小为32G了,两个内存占用量就是64GB,所以,我们在给Redis分配内存空间的时候,需要注意,需要给Redis预留一些空间内存。
通过RDB文件恢复数据
将dump.rdb 文件拷贝到redis的安装目录的bin目录下,重启redis服务即可。在实际开发中,一般会考虑到物理机硬盘损坏情况,选择备份dump.rdb 。
RDB的优缺点
RDB 的优点
体积更小:相同的数据量 RDB 数据比 AOF 的小,因为 RDB 是紧凑型文件。
恢复更快:因为 RDB 是数据的快照,基本上就是数据的复制,不用重新读取再写入内存。
性能更高:父进程在保存 RDB 时候只需要fork一个子进程,无需父进程的进行其他io操作,也保证了服务器的性能。
RDB 的缺点
故障丢失:因为 RDB 是全量的,我们一般是使用shell脚本实现30分钟或者1小时或者每天对 Redis 进行 RDB 备份,(注,也可以是用自带的策略),但是最少也要5分钟进行一次的备份,所以当服务死掉后,最少也要丢失5分钟的数据。
耐久性差:相对 AOF 的异步策略来说,因为 RDB 的复制是全量的,即使是 fork 的子进程来进行备份,当数据量很大的时候对磁盘的消耗也是不可忽视的,尤其在访问量很高的时候,主线程 fork 的时间也会延长,导致 cpu 吃紧,耐久性相对较差。
AOF持久化机制
AOF持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到回复数据的目的。AOF的主要作用是解决了数据持久化的实时性。
开启方式:
开启AOF功能需要设置配置: appendonly yes,默认不开启。AOF文件名通过appendfilename配置设置,默认文件名是appendonly.aof。保存路径同RDB持久化方式一致,通过dir配置指定。
AOF写入策略
系统调用write和fsync说明:
Write操作会触发延迟写机制。Linux在内核提供页缓冲区用来提高硬盘IO性能。wirte操作在写入系统缓冲区后直接返回。同步硬盘操作依赖于系统调度机制。例如:缓冲区页空间写满或达到特定时间周期。同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
fync针对单个文件操作(比如AOF文件),做强制硬盘同步,fsync将阻塞进程直到写入硬盘完成后返回,保证了数据持久化。
AOF写入流程
AOF追加阻塞
当开启AOF持久化时,常用的同步硬盘的策略是everysec,用于平衡性能和数据安全性。对于这种方式,Redis使用另一条线程每秒执行fsync同步硬盘。当系统硬盘资源繁忙时,会造成Redis主线程阻塞
阻塞流程分析:
1)主线程负责写入AOF缓冲区
2)AOF线程负责每秒执行一次同步磁盘操作,并记录最近一次同步时间
3)主线程负责对比上次AOF同步时间
如果距上次同步成功时间在2秒内,主线程直接返回。
如果距上次同步成功时间超过2秒,主线程将会阻塞,直到同步操作完成
AOF重写
随着命令不断写入AOF,文件会越来越大,为了解决这个问题,redis引入了AOF重写机制压缩文件体积。
触发机制
手动触发:
直接调用 bgrewriteaof 命令
自动触发:
根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage参数确定自动触发时机
auto-aof-rewrite-min-size 表示运行AOF重写时文件最小体积,默认为64MB
auto-aof-rewrite-percentage:代表当前AOF文件空间(aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的比值
自动触发时机=aof_current_size>auto-aof-rewrite-minsize&&(aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewritepercentage(其中aof_current_size和aof_base_size可以在info Persistence统计信息中查看)
AOF重写流程
1)执行AOF重写请求,如果当前进程正在执行AOF重写,请求不执行并返回如下响应:
ERR Background append only file rewriting already in progress
如果当前进程正在执行bgsave操作,重写命令延迟到bgsave完成之后再执行,返回如下响应:
Background append only file rewriting scheduled
2)父进程执行fork创建子进程,开销等同于bgsave过程
3)主进程fork操作完成后,继续响应其他命令。所有修改命令依然写
入AOF缓冲区并根据appendfsync策略同步到硬盘,保证原有AOF机制正确性
由于fork操作运用写时复制技术,子进程只能共享fork操作时的内
存数据。由于父进程依然响应命令,Redis使用“AOF重写缓冲区”保存这部
分新数据,防止新AOF文件生成期间丢失这部分数据
4)子进程根据内存快照,按照命令合并规则写入到新的AOF文件。每
次批量写入硬盘数据量由配置aof-rewrite-incremental-fsync控制,默认为
32MB,防止单次刷盘数据过多造成硬盘阻塞
5)新的AOF文件写入完成后,子进程发送信号给父进程,父进程更新
统计信息,具体见info persistence下的aof_*相关统计
父进程把AOF重写缓冲区的数据写入到新的AOF文件
使用新AOF文件替换老文件,完成AOF重写
当aof_buf和aof_rewrite_buf写满的时候是无法将进行一个持久化存储的操作的,所以我们在设置缓冲区大小时需要注意
AOF重写是基于我们当下这一刻内存中是有什么数据进行重写的,而不是根据命令log进行重写的。AOF重写时,看内存中有哪些数据,根据这些数据对应生成一个命令
AOF的优缺点分析
AOF 的优点
数据保证:我们可以设置fsync策略,一般默认是 Everysec,也可以设置每次写入追加,所以即使服务死掉了,也最多丢失一秒数据
自动缩小:当 AOF 文件大小到达一定程度的时候,后台会自动的去执行 AOF 重写,此过程不会影响主进程,重写完成后,新的写入将会写到新的 AOF 中,旧的就会被删除掉。但是此条如果拿出来对比 RDB 的话还是没有必要算成优点,只是官网显示成优点而已。
AOF 的缺点
性能相对较差:它的操作模式决定了它会对 Redis 的性能有所损耗。(主线程写文档)
体积相对更大:尽管是将 AOF 文件重写了,但是毕竟是操作过程和操作结果仍然有很大的差别,体积也毋庸置疑的更大。
恢复速度更慢:因为要重新加载每条命令的执行,恢复速度比较慢
RDB和AOF的比较:
RDB恢复数据的速度更快,当我们用redis作为DB的热点数据时,我们会同步的将一定权重的数据存储到DB中,即使我们服务器宕机宕掉之后,当我们重启后我们还是可以从DB中将数据加载到缓存中,这时候用RDB的方式进行恢复会更为合适。
当我redis的基本上的所有流量它基本上都是读的流量,写的流量比较少(小到一定的程度时),这种情况来说丢失的数据量会比较少,这时候我们用RDB进行持久化是比较合适的。
AOF的优点时就是数据的一致性比较好,当我们缓存的数据是DB中没有的时候,例如排行榜,数据只在redis中,当我丢失数据就会影响排行榜的排名,这时候我们就会使用AOF来进行一个持久化的工作。
现实生活中我们用的更多是AOF+RDB的一种方式:
RDB 和 AOF 两种方式也可以同时使用,在这种情况下,如果 redis 重启的话,则会优先采用 AOF 方式来进行数据恢复,这是因为 AOF 方式的数据恢复完整度更高。方式恢复数据时会优先查找看有没有RDB文件,如果有的话,就用RDB进行恢复,恢复速度快.
redis中定期删除的一些策略:
定期删除:设置一个时间,每隔一定的时间去检索所有的key,把所有过期的key进行一个删除。
惰性删除:我们把设置好的key放入内存中,当我们使用的时候,检测一下我们使用的key是不是过期了,如果过期了我们就删除。
实际情况下是使用定期删除和惰性删除连两个方法结合。