Redis恒久化:从AOF到RDB,怎样实现数据不丢失?

[复制链接]
发表于 前天 15:04 | 显示全部楼层 |阅读模式
Redis属于内存数据库,但为了防止宕机等导致的数据丢失,也有对应的数据恒久化技能。恒久化紧张作用就是数据备份,即将数据存储在硬盘,包管数据不会因历程退出而丢失。
AOF恒久化

Append Only File
雷同于Mysql的binlog日记雷同,会吧写利用下令以追加写的方式写入到AOF日记中。当重启redis后,先去读取这个文件里的下令,而且实验它,就相称于规复了缓存数据。
Redis写入日记过程图:


  • Redis 在实验完写利用下令后,并不会直接将下令写入到硬盘中的AOF日记中,由于如许将会产生大量的IO,而是会将下令追加到 server.aof_buf 缓冲区;
  • 然后通过 write() 体系调用,将 aof_buf 缓冲区的数据写入到 AOF 文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区 page cache,期待内核将数据写入硬盘;
  • 具体缓冲区的数据什么时间写入到硬盘,由写回计谋来决定。
三种写回计谋

Redis有三种写回计谋:

  • Always,每次写利用下令实验完后,总是会将 AOF 日记数据写回硬盘;
  • Everysec,每次写利用下令实验完后,先将下令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
  • No,意味着不由 Redis 控制写回硬盘的机会,转交给利用体系控制写回的机会,也就是每次写利用下令实验完后,先将下令写入到 AOF 文件的内核缓冲区,再由利用体系决定何时将缓冲区内容写回硬盘。
这三种写回计谋在源码中实在就是在控制fsync()方法的调用机会。
  1. if (sdslen(server.aof_buf) == 0) {//检查aof_buf中有没有数据
  2.     if (server.aof_fsync == AOF_FSYNC_EVERYSEC &&
  3.         server.aof_last_incr_fsync_offset != server.aof_last_incr_size &&
  4.         server.unixtime > server.aof_last_fsync &&
  5.         !(sync_in_progress = aofFsyncInProgress())) {
  6.         goto try_fsync;//控制每秒写回;异步执行,不影响主线程
  7.     } else if (server.aof_fsync == AOF_FSYNC_ALWAYS &&
  8.                server.aof_last_incr_fsync_offset != server.aof_last_incr_size){
  9.         goto try_fsync;//总是写回;由主线程执行,未返回会阻塞主线程
  10.     } else {
  11.                 //redis不控制写回,最终交给操作系统决定何时写回;不影响主线程
  12.         return;
  13.     }
  14. }
复制代码
显然Always写回计谋是由主历程实验的,总是调用fsync函数;Everysec异步实验,不影响主线程;No则redis不控制写回,终极交给利用体系决定何时写回;不影响主线程
fsync()函数会将内存中修改的数据和文件形貌符的属性恒久化到存储装备中,而且比及硬盘写利用完成后,该函数才会返回
三种写回计谋的优缺点:

AOF 重写机制

重写机制紧张就是为了压缩AOF文件的巨细,当 AOF 文件的巨细凌驾所设定的阈值后,Redis 就会启用 AOF 重写机制,来压缩 AOF 文件。
  1. //表示当前AOF文件空间(aof_current_size) 和上一次重写后AOF文件空间(aof_base_size) 的比值。
  2. auto-aof-rewrite-percentage 100
  3. //表示运行AOF重写时文件最小体积, 默认为64MB。
  4. auto-aof-rewrite-min-size 64mb
复制代码
AOF 重写机制是在重写时,读取当前数据库中的全部键值对,然后将每一个键值对用一条下令记载到 新的 AOF 文件,比及全部记载完后,就将新的 AOF 文件更换掉现有的 AOF 文件。
重写机制的原理:假如某个键值对被多条写下令反复修改,终极也只必要根据这个键值对当前的最新状态,然后用一条下令去记载键值对,代替之前记载这个键值对的多条下令,如许就淘汰了 AOF 文件中的下令数量。
重写时为什么不复用当前AOF?
假如 AOF 重写过程中失败了,现有的 AOF 文件就会造成污染,大概无法用于规复使用。
Redis 的重写 AOF 过程是由配景子历程 bgrewriteaof 来完成的
RDB快照

RDB 快照就是记载某一个刹时的内存数据,记载的是实际数据,也就是说RDB是全量快照,也就是说每次实验RDB,都是把内存中的全部数据都记载到磁盘中。当必要规复数据时, RDB 规复数据的服从也会比 AOF 高些,由于直接将 RDB 文件读入内存就可以,不必要像 AOF 那样还必要额外实验利用下令的步调才气规复数据。
由于RDB是全量快照,因此不发起过于频仍,但频率过低也会导致丢失的数据更多。
实验下令

RDB全量模式恒久化将数据写入磁盘的动作可以分为SAVE与BGSAVE两种。所谓BGSAVE就是background-save,也就是配景异步save,区别点在于SAVE是由Redis的下令实验线程按照平凡下令的方式去实验利用,而BGSAVE是通过fork出一个新的历程,在新的独立历程内里去实验save利用。

Redis的哀求下令实验是通过单线程的方式实验的,以是要只管制止耗时利用,而save动作必要将内存全部数据写入到磁盘上,对于redis而言,这一利用是非常耗时的,会壅闭住全部正常业务哀求,以是save利用的触发只有两个场景:

  • 客户端手动发送save下令实验
  • Redis在shutdown的时间自动实验
从数据生存完备性方面看,这两种方式都起不到自动恒久化备份的本领,假如出现一些呆板掉电等情况,是不会触发redis shutdown利用的,将面对数据丢失的风险。
相比而言,bgsave的杀伤力要小一些、实用度也更好一些,它可以包管在恒久化期间Redis主历程可以继续处理处罚业务哀求。bgsave增长了过程中自动恒久化利用的机制,触发条件更加的“智能”:

  • 客户端手动下令触发bgsave利用
  • Redis设置定时使命触发(支持隔断时间+变更数据量双重维度综合判断,到达任一条件则触发)
别的,在master-slave主从摆设的场景中还支持仅由slave节点触发bgsave利用,来低沉对master节点的影响。
写时复制技能

Redis可以实验bgsave,将天生RDB的工作交给子历程来做,此时Redis主线程还可以继续处理处罚利用下令。Redis为了实现配景把内存数据的快照写入文件,采取了利用体系提供的Copy On Write写时复制技能,也就是fork体系调用。
写时复制大抵过程如下:

  • fork体系调用会产生一个子历程,与父历程共享雷同的内存地点空间,如许历程在这一时候就能拥有与父历程的雷同的内存数据。
  • 固然子历程与父历程共享同一块内存地点空间,但在fork子历程时,利用体系必要拷贝父历程的内存页表给子历程,假如整个Redis实例内存占用很大,那么它的内存页表也会很大,在拷贝时就会比力耗时,同时这个过程会斲丧大量的CPU资源。在完成拷贝之前父历程也处于壅闭状态,无法处理处罚客户端哀求。
  • fork实验完之后,子历程就可以扫描自身全部的内存数据,然后把全部数据写入到RDB文件中。
  • 之后父历程仍旧处理处罚客户端的哀求,当在处理处罚写下令时,父历程会重新分配新的内存地点空间,从利用体系申请新的内存使用,不再与子历程共享,这个过程就是Copy On Write(写实复制)名字的由来。如许父子历程的内存就会渐渐分离,父历程申请新的内存空间并更改内存数据,子历程的内存数据不受影响。
比如:当主线程要修改共享数据里的某一块数据(比如键值对 A)时,就会发生写时复制,那么这块数据的物理内存就会被复制一份(键值对 A'),bgsave 子历程可以把原来的数据(键值对 A)写入到 RDB 文件中。与此同时,主线程可以在这个数据副本(键值对 A')举行修改利用。
为了包管天生RDB时还能实验利用下令,引入的写时复制技能,但显然写时复制技能也有其缺点:

  • 在天生RDB的过程中,假如主线程修改了内存数据,RDB 快照无法写入主线程刚修改的数据,假如此时体系宕机了,也就丢失了这部分修改的数据
  • 非常情况下,全部数据都被修改,那么由于写时复制技能,内存占用将会是原来的两倍。假如呆板剩余内存不敷,则大概导致fork的时间两份内存数据量凌驾呆板物理内存巨细,导致体系启用捏造内存,拷贝速率大打扣头(捏造内存本质上就是把磁盘当内存用,利用速率相比物理内存大大低沉),会壅闭住Redis主历程的下令实验
底层的实现仅仅复制了页表,但映射的物理内存照旧同一个。如许做可以加速 fork 的速率,淘汰性能斲丧(fork会壅闭主历程)。

留意:制止高峰期天生 RDB,假如 RDB 时间长,且写并发高,此时会被体系产生比力大的影响。缘故原由是由于写时复制时,假如共享的每一页内存都被修改,就会使得内存极速膨胀,最大内存可以膨胀两倍,以是要留意内存的使用量,防止内存过载,RDB 会产生大量的磁盘 I/O,要留意磁盘性能导致的影响。还必要留意 CPU 负载,毕竟有大量的数据必要写入。因此假如 RDB 在高峰期大概会影响到正常业务,必要公道安排天生 RDB 的机会。
总结

区别:

  • 记载的数据不一样:

    • RDB 快照就是记载某一个刹时的内存数据,记载的是实际数据,而 AOF 文件记载的是下令利用的日记
    • AOF 文件的内容是利用下令;RDB 文件的内容是二进制数据

  • 规复数据和实验频率:

    • RDB是全量快照,规复数据更快,AOF则必要额外实验利用下令,相对更慢。
    • RDB是全量快照,不宜频仍实验,而AOF数据文件更新比力实时,比RDB生存更完备的数据,如许在数据规复时可以大概规复只管完备的数据,低沉丢失数据的风险。因此发生故障时,RDB丢失的数据会比 AOF 恒久化的方式更多

  • 是否影响主历程

    • AOF的Always写回计谋是主历程实验的,总是调用fsync函数;Everysec异步实验,不影响主线程;No则redis不控制写回,终极交给利用体系决定何时写回;不影响主线程。
    • RDB可以将工作交给子历程来做,此时Redis主线程还可以继续处理处罚利用下令。

假如同时存在RDB文件和AOF文件,Redis会优先使用AOF文件举行数据规复。
肴杂恒久化

RDB 比 AOF 的数据规复速率快,但是快照的频率不好把握:

  • 假如频率太低,两次快照间一旦服务器发生宕机,就大概会比力多的数据丢失;
  • 假如频率太高,频仍写入磁盘和创建子历程会带来额外的性能开销。
肴杂恒久化就是肴杂使用 AOF 日记和RDB
肴杂恒久化工作在 AOF日记重写过程中:
会把 Redis 的恒久化数据,以 RDB 的格式写入到 AOF 文件的开头,之后写时复制时修改数据再以 AOF 的格式化追加的文件的末了,写入完成后再新的含有 RDB 格式和 AOF 格式的 AOF 文件更换旧的的 AOF 文件。
也就是说,使用了肴杂恒久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据。

Redis规复数据源码:
(AOF 格式的开头是 *,而 RDB 格式的开头是 REDIS。)
  1. if (fread(sig,1,5,fp) != 5 || memcmp(sig,"REDIS",5) != 0) {
  2.     // AOF 文件开头非 RDB 格式,非混合持久化文件
  3.     if (fseek(fp,0,SEEK_SET) == -1) goto readerr;
  4. } else {
  5.     /* RDB format. Pass loading the RDB functions. */
  6.     rio rdb;
  7.     int old_style = !strcmp(filename, server.aof_filename);
  8.     if (old_style)
  9.         serverLog(LL_NOTICE, "Reading RDB preamble from AOF file...");
  10.     else
  11.         serverLog(LL_NOTICE, "Reading RDB base file on AOF loading...");
  12.     if (fseek(fp,0,SEEK_SET) == -1) goto readerr;
  13.     rioInitWithFile(&rdb,fp);
  14.         // AOF 文件开头是 RDB 格式,先加载 RDB 再加载 AOF
  15.     if (rdbLoadRio(&rdb,RDBFLAGS_AOF_PREAMBLE,NULL) != C_OK) {
  16.         if (old_style)
  17.             serverLog(LL_WARNING, "Error reading the RDB preamble of the AOF file %s, AOF loading aborted", filename);
  18.         else
  19.             serverLog(LL_WARNING, "Error reading the RDB base file %s, AOF loading aborted", filename);
  20.         ret = AOF_FAILED;
  21.         goto cleanup;
  22.     } else {
  23.         loadingAbsProgress(ftello(fp));
  24.         last_progress_report_size = ftello(fp);
  25.         if (old_style) serverLog(LL_NOTICE, "Reading the remaining AOF tail...");
  26.     }
  27. }
复制代码
优点:

  • 肴杂恒久化联合了 RDB 和 AOF 恒久化的优点,开头为 RDB 的格式,使得 Redis 可以更快的规复数据,同时联合 AOF 的优点,减低了大量数据丢失的风险。
缺点:

  • AOF 文件中添加了 RDB 格式的内容,使得 AOF 文件的可读性变得很差;
  • 兼容性差,假如开启肴杂恒久化,那么此肴杂恒久化 AOF 文件,就不能用在 Redis 4.0 之前版本了。

免责声明:如果侵犯了您的权益,请联系站长及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表