Redis——持久化
目录RDB手动触发非阻塞式阻塞式自动触发AOF开启AOFAOF文件AOFAOF三种策略always 策略everysec 策略默认no 策略AOF文件的重写重写流程手动重写自动重写混合持久化Redis持久化有两种策略RDB和AOF。RDB是定期备份AOF是每次修改都备份。RDB不保证安全AOF可以保证相对安全RDBRDB有两种定期备份的途经一种是执行redis命令手动触发另一种是通过修改配置文件让redis自动定期备份。手动触发非阻塞式相关命令//非阻塞式备份期间可以执行其他命令 bgsave执行流程redis使用新启动一个进程而不是线程的方式来进行后台备份。redis数据存放于内存如果使用线程备份线程和主线程访问同一块内存此时需要加锁、阻塞主线程。而使用进程的话由于写时拷贝机制调用bgsave时内存的状况会被完整保存不会被中途修改不需要进行加锁。所以使用进程是更加合适的执行 bgsave 命令时Redis 父进程首先判断当前是否存在其他正在执行的子进程如 RDB/AOF 子进程若存在则 bgsave 命令直接报告执行失败。这是因为多个bgsave同时执行没有意义数据差异并不大而且还消耗很多CPU、内存资源父进程执行 fork 创建子进程fork 过程中父进程会阻塞但是备份的过程不会阻塞主进程。父进程 fork 完成后bgsave 命令返回 Background saving started 信息不再阻塞父进程父进程可继续响应其他命令子进程开始备份。子进程创建 RDB 文件存放于redis工作目录下可通过配置文件修改根据父进程内存生成临时快照文件完成后对原有RDB文件进行原子替换。为什么要这样因为如果直接往原有RDB文件写入的话一但这次bgsave被打断原有的RDB文件就会被破坏使得所有数据都无效RDB文件RDB文件里存放的是压缩过后的redis数据压缩可以节省空间但是复原需要消耗CPU存储的直接是二进制。redis-check-rdb命令可以检查一个rdb文件是否正确如果rdb文件是错误的可能会导致数据错乱甚至服务器无法启动。阻塞式//阻塞式备份期间无法执行其他命令 save自动触发redis可以设置每隔多久或者每修改几次进行一次自动备份通过配置文件修改自动触发的时机当然除了自己设置的触发时机在服务器被正常关停等情况也会触发自动备份。如果修改数据后没触发自动备份也没手动备份并且redis服务崩掉了那修改就会丢失。AOF一但AOF开启redis服务器优先使用AOF文件进行数据恢复而不是RDB文件。开启AOF在redis.conf这个配置文件修改配置即可AOF文件AOF文件以文本方式存放redis的操作记录并不直接存二进制键值对。因此使用AOF文件恢复数据要比RDB文件要慢很多。每次写磁盘就是在AOF文件末尾添加操作记录不会修改文件中间的记录。正是因此AOF模式下备份不需要经过“写到临时文件-然后再把原文件删除-给临时文件改名”这样的过程就算redis服务突然崩了也仅仅只是让某一条操作记录损坏而不会影响前面所有的记录。AOF有可读性好、容易修复因为是命令文本等优点。AOFAOF理论上是每一次修改都会写磁盘但实际上在AOF模式下修改会首先被存入内存中的aof_buff缓冲区一次时间循环后才会写磁盘。AOF提供了三种策略分别对应三种安全级别安全性越高效率越低具体如何选择取决于业务AOF三种策略always策略aof_buf刷新时机每个写命令执行完毕后立即调用flushAppendOnlyFile函数该函数会执行write将当前aof_buf中的所有命令写入内核缓冲区然后清空aof_buf。⏱️ 频率每个写命令后一次。内核缓冲区刷新时机同一个调用过程中write之后立刻执行fsync强制将本次写入内核缓冲区的数据同步到磁盘。⏱️ 频率每个写命令后立即fsync。结论在always下aof_buf一有数据就被write并清空且内核缓冲区立刻被fsync两者几乎同步发生数据最安全但性能最低。everysec策略默认aof_buf刷新时机Redis 主线程在每次事件循环的beforeSleep阶段处理完一批客户端命令后准备进入多路复用的等待前都会调用flushAppendOnlyFile。该函数将aof_buf中的所有数据通过write写入内核缓冲区并清空aof_buf。只要事件循环在运行这个动作就会非常频繁——可能每处理几个命令就发生一次远高于每秒一次。⏱️ 频率每个事件循环批次一次通常每毫秒级都可能发生。内核缓冲区刷新时机flushAppendOnlyFile中不会直接执行fsync。而是由 Redis 专门启动的一个后台线程每隔 1 秒钟调用一次fsync将这一秒内内核缓冲区中积累的 AOF 数据强制刷盘。如果主线程执行write时发现上一次后台fsync尚未完成为了不阻塞主线程它甚至会跳过本次write即不写入内核缓冲区aof_buf不清空等下次再写以保证主线程的响应速度。⏱️ 频率后台线程每秒执行一次fsync。结论everysec下aof_buf不断被快速清空并写入内核缓冲区减少内存积压但内核缓冲区的数据会积攒最多 1 秒再由fsync批量刷盘。这是一种极好的平衡性能较高最多丢失 1-2 秒的数据。no策略aof_buf刷新时机与everysec完全相同同样在事件循环的beforeSleep中调用flushAppendOnlyFile将aof_buf的内容write到内核缓冲区并清空aof_buf。⏱️ 频率每个事件循环批次一次。内核缓冲区刷新时机Redis 完全不调用fsync。内核缓冲区的数据何时写入磁盘完全由操作系统自行决定。在 Linux 系统中通常会由内核线程pdflush定期比如每 30 秒或在脏页比例达到阈值时将页缓存刷新到磁盘。⏱️ 频率操作系统决定典型约 30 秒一次。结论no策略下aof_buf依然被频繁地write并清空但内核缓冲区很可能长时间不刷盘一旦宕机可能丢失最近数十秒的数据性能最高安全性最低。修改redis.conf这个配置文件即可AOF文件的重写这个主要是可以缩小AOF文件的体积加快reids服务的启动时间以及节省磁盘空间。缩小的方式就是等价set key 111和del key 就等价于什么都不做。AOF文件的重写可以手动重写和自动重写。实际上AOF重写是直接把内存中的状态保存下来并不会遍历原有的AOF文件进行等价缩小这样太慢了。重写流程如果当前进程正在执行 AOF 重写则新请求不执行因为很短时间内多次重写没有意义。如果当前进程正在执行 bgsave 操作则重写命令延迟到 bgsave 完成之后再执行。首先重写并不是特别重要知识一种优化其次两个一同执行可能会导致资源消耗峰值更高造成redis服务崩溃父进程执行 fork 创建子进程用于重写。重写过程父进程 fork 之后继续响应其他命令。所有修改操作同时写入两个缓冲区常规 AOF 缓冲区aof_buff和 AOF 重写缓冲区aof_rewrite_buff。子进程仅拥有 fork 时刻的内存快照根据内存快照将数据转换为命令并合并写入临时 AOF 文件。临时文件写入完成后子进程发送信号通知父进程。父进程将 AOF 重写缓冲区中临时保存的命令追加到临时文件末尾。用临时文件原子替换旧 AOF 文件。为什么父进程还要把修改写入aof_rewrite_buff缓冲区子进程进行aof重写的同时父进程可以执行其他命令但是子进程看不到这些修改也就不会把这些修改对应的操作记录保存到临时文件中。那么如果原有AOF文件被临时文件替换就会造成一部分的修改丢失并且假设此时aof_buff因为数据都被写到旧AOF文件而清空在后续的AOF写入中也不会把这部分修改存进去。但是重写被触发后会把这部分数据给填补上因为重写是直接将内存数据转换成命令存放并不依赖旧的AOF文件但是一但断电或者服务崩掉内存中的数据也丢失了直接造成这部分数据永远丢失。任何AOF策略都不能保证不丢失这不符合AOF的初衷。把子进程重写期间父进程所做的修改存到aof_rewrite_buff缓冲区中等到子进程重写成功后父进程把aof_rewrite_buff缓冲区的内容追加到临时文件末尾。在此期间不会有命令被执行redis单线程。这就支持了AOF模式下的安全性。为什么重写要涉及临时文件而普通备份则不很简单重写会大范围修改AOF文件影响太大。手动重写bgrewriteaof自动重写配置项 1auto-aof-rewrite-percentage作用触发重写的增长率阈值默认值100说明当前AOF文件体积比上次重写后增长了多少百分比时触发。设置为 0 可禁用自动重写。配置项 2auto-aof-rewrite-min-size作用触发重写的文件大小下限默认值64mb说明避免在AOF文件还很小的时候就频繁重写。混合持久化就是在触发AOF重写后会把当前内存中的数据按照RDB文件的那种二进制压缩格式来存放以此来加快服务重启速度以及进一步缩小体积。而不是存放操作记录。但是AOF下的普通备份仍旧是按照文本方式在AOF文件中追加操作记录AOF备份比较频繁如果AOF每次备份都直接存放二进制数据那么必须使用临时文件保证大部分数据不会失效但是这样效率会大大降低。在混合持久化情况下AOF文件中可能会出现两种格式的内容