乐趣区

关于后端:非看不可的Redis持久化

本文已收录至 Github,举荐浏览 👉 Java 随想录
微信公众号:Java 随想录

写在后面

Redis 的长久化,这部分的知识点不仅求职面试的时候是重点,工作中也是常常打交道。说起长久化都会想到 RDB 和 AOF,然而外面有些细节是能够开展去聊的。比方:为什么 fork 速度这么快?AOF 是如何进步写入性能的?等问题。对这些疑难本文都会有所解答。

摘要

Redis 是许多公司都在应用的一款高性能、非关系型数据库。其中最为重要的一个个性就是它反对长久化。本文将深刻介绍 Redis 长久化原理,包含 RDB 和 AOF 两种形式的实现。

Redis 长久化介绍

Redis 长久化分为两种:RDB(Redis DataBase)和 AOF(Append Only File)。RDB 是指将 Redis 内存中的数据定期写入磁盘上的一个快照文件中;而 AOF 则是以追加的形式记录 Redis 执行的每一条写命令。 你也能够同时开启两种长久化形式,在这种状况下,当 redis 重启的时候会优先载入 AOF 文件来复原原始的数据

你兴许会问,为什么须要长久化呢?因为 Redis 作为一款内存数据库,在过程异样退出或服务器断电之后,所有的数据都将隐没。如果没有长久化性能,无奈保证数据的持久性,那么这样的数据库还有什么用呢?

因而,Redis 提供了长久化性能,以确保数据的可靠性和持久性。接下来,咱们将别离介绍 RDB 和 AOF 的实现原理。

RDB 原理

RDB 是 Redis 默认的长久化形式,它将 Redis 在内存中的数据定期写入到硬盘中,生成一个快照文件。快照文件是一个二进制文件,蕴含了 Redis 在某个工夫点的所有数据。

RDB 的长处是疾速、简略,实用于大规模数据备份和复原。然而,RDB 也有毛病,例如数据可能会失落,因为 Redis 只会在指定的工夫点生成快照文件。如果在快照文件生成之后,但在下一次快照文件生成之前服务器宕机,那么这期间的数据就会失落

因为 RDB 文件是以二进制格局保留的,因而它十分紧凑,并且在 Redis 重启时能够迅速地加载数据。相比于 AOF,RDB 文件个别会更小。

RDB 长久化有两种形式:

  • 手动
  • 主动

手动形式通过 SAVE 命令或 BGSAVE 命令进行。SAVE 命令会阻塞 Redis 服务器,直到快照文件生成实现。BGSAVE 命令会 fork 一个子过程(留神是子过程,不是子线程)在后盾生成快照文件,不会阻塞 Redis 服务器。

主动形式则是在配置文件中设置,让它在“N 秒内数据集至多有 M 个改变”这一条件被满足时,主动保留一次数据集。

比如说,以下设置会让 Redis 在满足“10 秒内有至多 100 个键被改变”这一条件时,主动保留一次数据集。

save 10 100

Fork 函数与写时复制

在 Redis 中,fork 函数被用于创立子过程。Redis 的应用场景中通常有大量的读操作和较少的写操作,而 fork 函数能够利用 Linux 操作系统的写时复制(Copy On Write,即 COW)机制,让父子过程共享内存,从而缩小内存占用,并且防止了没有必要的数据复制。

咱们能够应用 man fork 来查看下阐明文档。

翻译:在 Linux 下,fork()是应用写时复制的页面实现的,所以它惟一的代价是复制父过程的页表以及为子过程创立独特的工作构造所需的工夫和内存

简略来说就是 fork()函数复制的是指针,不会复制数据,所以速度很快。

须要留神的是,fork 的这个过程主过程是阻塞的,fork 完之后不阻塞

在 Redis 中,当执行 RDB 长久化操作时,Redis 会调用 fork 函数创立子过程,而后由子过程负责将数据写入到磁盘中。为了防止父子过程同时对内存中的数据进行批改导致数据不统一。Redis 会启用写时复制机制。这样,当父过程批改内存中的数据时,Linux 内核会将该局部内存复制一份给子过程应用,从而保障父子过程间的数据相互独立

简略画了个示意图:

当没有产生写的时候,子过程和父过程指向地址是一样的,产生写的时候,就会拷贝出一块新的内存区域,实现父子过程隔离

通过应用 fork 函数和写时复制机制,Redis 能够高效地执行 RDB 长久化操作,并且不会对 Redis 运行过程中的性能造成太大的影响。同时,这种形式也提供了一种简略无效的机制来爱护 Redis 数据的一致性和可靠性。

毛病:RDB 须要常常 fork 子过程来保留数据集到硬盘上,当数据集比拟大的时候,fork 的过程是十分耗时的,可能会导致 Redis 在一些毫秒级内不能响应客户端的申请,数据集很大的时候,fork 过程可能会继续数秒

对于写时复制的思考

上述流程貌似有个问题。比方,有个键值对 k1 a。此时正在 bgsave,客户端发来一个申请,主过程产生写操作set k1 b,因为写时复制,此时子过程里 k1 值还是值 a。最终长久化的也是 a。

为什么不间接长久化新值而长久化旧值?写时复制的意义是什么?

次要有 2 点起因:

  1. 其实 Redis 为了性能思考,将内存的数据长久化是一个程序写的操作,RDB 备份是有一个过程的,这个过程是由子过程实现。子过程备份 RDB 是一个程序写的过程,如果主过程的所有写入申请都随时记录到 RDB 文件中,那么实践更新 key 可能在任何地位,这是个随机写的过程,性能低。
  2. 其次如果主过程始终在写入的话,那么这次 RDB 备份始终都在写主过程写入的新值,永远不会进行。

RDB 相干配置

  1. save:指定 RDB 长久化操作的条件。当 Redis 的数据发生变化,并且通过指定的工夫(seconds)和变动次数(changes)后,Redis 会主动执行一次 RDB 操作。例如,save 3600 10000 示意如果 Redis 的数据在一个小时内产生了至多 10000 次批改,那么 Redis 将执行一次 RDB 操作。
  2. stop-writes-on-bgsave-error:指定在 RDB 长久化过程中如果呈现谬误是否进行写入操作。如果设置为 yes,当 Redis 在执行 RDB 操作时遇到谬误时,Redis 将进行承受写入操作;如果设置为 no,Redis 将持续承受写入操作。
  3. rdbcompression:指定是否对 RDB 文件进行压缩。如果设置为 yes,Redis 会在生成 RDB 文件时对其进行压缩,从而缩小磁盘占用空间;如果设置为 no,Redis 不会对生成的 RDB 文件进行压缩。
  4. rdbchecksum:指定是否对 RDB 文件进行校验和计算。如果设置为 yes,在保留 RDB 文件时,Redis 会计算一个 CRC64 校验和并将其追加到 RDB 文件的开端;在加载 RDB 文件时,Redis 会对文件进行校验和验证,以确保文件没有受到损坏或篡改。
  5. replica-serve-stale-data:这是 Redis 4.0 中新增的一个配置项,用于指定复制节点在与主节点断开连接后是否持续向客户端提供旧数据。当设置为 yes 时,在复制节点与主节点断开连接后,该节点将持续向客户端提供旧数据,直到从新连贯上主节点并且同步齐全新的数据为止;当设置为 no 时,复制节点会立刻进行向客户端提供数据,并且期待从新连贯上主节点并同步数据。须要留神的是,当 replica-serve-stale-data 设置为 yes 时,可能会存在肯定的数据不一致性问题,因而倡议仅在特定场景下应用。
  6. repl-diskless-sync:这是 Redis 2.8 中引入的一个配置项,用于指定复制节点在进行首次全量同步(即从主节点获取全副数据)时是否采纳无盘同步形式。当设置为 yes 时,复制节点将通过网络间接获取主节点的数据,并且不会将数据存储到本地磁盘中;当设置为 no 时,复制节点将先将主节点的数据保留到本地磁盘中,而后再进行同步操作。采纳无盘同步形式能够防止磁盘 IO 操作对系统性能的影响,但同时也会减少网络负载和内存占用。因而,应该依据具体的场景和需要抉择适合的同步形式

AOF 原理

AOF 长久化是依照 Redis 的写命令程序将写命令追加到磁盘文件的开端。AOF 是一种基于日志的长久化形式,它保留了 Redis 服务器所有写入操作的日志记录,以保证数据的持久性、可靠性和完整性。AOF 长久化技术的核心思想是将 Redis 服务器执行的所有写命令追加到一个文件中。当 Redis 服务器重新启动时,能够通过从新执行 AOF 文件来复原服务器的状态。

AOF 有个比拟好的劣势是能够复原误操作。举个例子,如果你不小心执行了 FLUSHALL 命令,但只有 AOF 文件未被重写,那么只有进行服务器,移除 AOF 文件开端的 FLUSHALL 命令,并重启 Redis,就能够将数据集复原到 FLUSHALL 执行之前的状态,重写了就没方法了。

AOF 长久化配置

Redis 的 AOF 长久化配置频率可通过appendfsync 参数进行管制。该参数有以下三个选项:

  1. always: 每次有数据批改都立刻写入磁盘,是最平安的选项。
  2. everysec: 每秒钟写入一次,性能和平安之间做了一个均衡。
  3. no: 从不被动写入,齐全依附操作系统本身的缓存机制来决定何时将数据写入磁盘。

默认状况下,Redis 的 appendfsync 参数设置为 everysec。如果须要进步长久化安全性,能够将其改为always;如果更关注性能,则能够将其改为no。然而须要留神的是,应用no 可能会导致数据失落的危险,倡议在利用场景容许的状况下审慎应用。

AOF 文件解读

这是一个简略的 AOF 文件示例。

  • * 号:示意参数个数,前面紧跟着参数的长度和值。
  • $ 号:示意参数长度,前面紧跟着参数的值。

实际上 AOF 文件中保留的所有命令都遵循雷同的格局,即以 * 结尾示意参数个数,$ 结尾示意参数长度,其后紧跟着参数的值。

AOF 文件修复

服务器可能在程序正在对 AOF 文件进行写入时停机,造成 AOF 文件出错。

能够应用 Redis 自带的 redis-check-aof 程序,对原来的 AOF 文件进行修复:

$ redis-check-aof –fix

AOF 重写

Redis 的 AOF 重写机制指的是将 AOF 文件中的冗余命令删除,以减小 AOF 文件的大小并进步读写性能的过程。

Redis 的 AOF 重写机制采纳了相似于复制的形式,首先将内存中的数据快照保留到一个临时文件中,而后遍历这个临时文件,只保留最终状态的命令,生成新的 AOF 文件。

具体来说,Redis 执行 AOF 重写能够分为以下几个步骤:

  1. 开始 AOF 重写过程,向客户端返回一个提示信息。
  2. 创立一个临时文件,并将以后数据库中的键值对写入到临时文件中。
  3. 在创立的临时文件中将所有的写命令都转换成 Redis 外部的示意格局,即应用一系列的 Redis 命令来示意一个操作,例如应用 SET 命令来示意对某个键进行赋值操作。
  4. 对临时文件进行压缩,去掉多余的空格和换行符等,减小文件体积。
  5. 将压缩后的内容写入到新的 AOF 文件中。
  6. 进行写入命令到旧的 AOF 文件,并将新的 AOF 文件的文件名替换为旧的 AOF 文件的文件名。
  7. 完结 AOF 重写过程,并向客户端发送实现提示信息。

通过 AOF 重写机制,Redis 能够在不进行服务的状况下减小 AOF 文件的大小,进步读写性能,同时也能够保证数据的一致性。

Redis 提供了手动触发 AOF 重写的命令BGREWRITEAOF。能够在 Redis 的客户端中执行该命令来启动 AOF 重写过程。Redis 2.2 须要本人手动执行 BGREWRITEAOF 命令;Redis 2.4 则能够主动触发 AOF 重写。

具体操作步骤如下:

  1. 关上 redis-cli 命令行工具,连贯到 Redis 服务。
  2. 执行 BGREWRITEAOF 命令,启动 AOF 重写过程。

    $ redis-cli
    127.0.0.1:6379> BGREWRITEAOF
  3. Redis 会返回一个后台任务的 ID,示意 AOF 重写工作曾经开始。

    127.0.0.1:6379> BGREWRITEAOF
    Background append only file rewriting started by pid 1234
  4. 能够应用 INFO PERSISTENCE 命令查看以后 AOF 文件的大小和重写过程的状态,期待重写实现即可。

    127.0.0.1:6379> INFO PERSISTENCE
    # Persistence
    aof_enabled:1
    aof_rewrite_in_progress:1
    aof_rewrite_scheduled:0
    aof_last_rewrite_time_sec:0
    aof_current_rewrite_time_sec:14
    aof_last_bgrewrite_status:ok
    aof_last_write_status:ok

须要留神的是,执行 BGREWRITEAOF 命令可能会占用较多的 CPU 和内存资源,因而在生产环境中须要审慎应用,并确保有足够的系统资源反对。同时,即便手动触发 AOF 重写,Redis 也会在满足肯定条件时主动触发 AOF 重写,以保障 AOF 文件的大小和性能。

在版本号大于等于 2.4 的 Redis 中,BGSAVE 执行的过程中,不能够执行 BGREWRITEAOF。反过来说,在 BGREWRITEAOF 执行的过程中,也不能够执行 BGSAVE。这能够避免两个 Redis 后盾过程同时对磁盘进行大量的 I/O 操作

AOF 缓冲区与 AOF 重写缓存区

在 Redis 中,AOF 缓冲区和 AOF 重写缓冲区是两个不同的概念。

AOF 缓冲区是一个用于暂存须要写入 AOF 文件的命令的缓冲区。在 Redis 解决客户端发来的写命令时,如果开启了 AOF 长久化性能,则该命令将被先写入到 AOF 缓冲区。AOF 缓冲区中的内容通过配置的规定长久化到磁盘上。长久化规定能够通过配置项 appendfsync 来调整。

AOF 重写缓冲区是一个用于执行 AOF 文件的重写操作的缓冲区。AOF 重写操作是一种将现有 AOF 文件重写成最小化的新 AOF 文件的操作。AOF 重写操作的目标是缩小 AOF 文件的大小,同时放慢复原速度。AOF 重写缓存区在 AOF 重写时开始启用,Redis 服务器主过程在执行完写命令之后,会同时将这个写命令追加到 AOF 缓冲区和 AOF 重写缓冲区

须要留神的是,AOF 缓冲区和 AOF 重写缓冲区是不同的概念,但它们都应用了 Redis 的内存缓冲机制,并且都会影响 Redis 服务器的性能。因而,在理论应用中,应该合理配置 AOF 缓冲区大小和 AOF 重写规定,以确保 Redis 服务器的失常运行和高性能。

AOF 缓冲区能够代替 AOF 重写缓冲区吗?

AOF 缓冲区不能够代替 AOF 重写缓冲区的起因是AOF 重写缓冲区记录的是从重写开始后的所有须要重写的命令,而 AOF 缓冲区可能只记录了局部的命令(如果写回的话,AOF 缓存区的数据就会生效被失落,因此只会保留一部分的命令,而 AOF 重写缓存区不会)

AOF 缓冲区就次要是 Redis 用来解决主过程执行命令速度与磁盘写入速度不同步所设置的,通过 AOF 缓冲区能够无效地防止频繁对硬盘进行读写,进而晋升性能。Redis 在 AOF 长久化的时候,会先把命令写入到 AOF 缓冲区,而后通过回写策略来写入硬盘 AOF 文件。

AOF 相干配置

在 Redis 的配置文件 redis.conf 中,能够通过以下配置项来设置 AOF 相干参数:

  1. appendonly:该配置项用于开启或敞开 AOF,默认为敞开。若开启了 AOF,Redis 会在每次执行写命令时,将命令追加到 AOF 文件开端。
  2. appendfilename:用于设置 AOF 文件名,默认为 appendonly.aof。
  3. appendfsync:该配置项用于设置 AOF 的同步机制。有三种可选值:

    • always:示意每个写命令都要同步到磁盘,安全性最高,然而性能较差。
    • everysec:示意每秒同步一次,是默认选项,既能保障数据安全,又具备较好的性能。
    • no:示意不进行同步,而是由操作系统决定何时将缓冲区中的数据同步到磁盘上,性能最好,然而安全性较低。
  4. auto-aof-rewrite-percentageauto-aof-rewrite-min-size:这两个配置项用于设置 AOF 重写规定。当 AOF 文件大小超过 auto-aof-rewrite-min-size 设置的值,并且 AOF 文件增长率达到 auto-aof-rewrite-percentage 所定义的百分比时,Redis 会启动 AOF 重写操作。auto-aof-rewrite-percentage:默认值为 100,以及 auto-aof-rewrite-min-size:64mb 配置,也就是说默认 Redis 会记录上次重写时的 AOF 大小, 默认配置是当 AOF 文件大小是上次 rewrite 后大小的一倍且文件大于 64M 时触发。
  5. aof-use-rdb-preamble:Redis 4 版本新个性,混合长久化。AOF 重写期间是否开启增量式同步,该配置项在 AOF 重写期间是否应用 RDB 文件内容。默认是 no,如果设置为 yes,在 AOF 文件头退出一个 RDB 文件的内容,能够尽可能的减小 AOF 文件大小,同时也不便复原数据。

写后日志

咱们比拟相熟的是数据库的写前日志(Write Ahead Log,WAL),也就是说,在理论写数据前,先把批改的数据记到日志文件中,以便故障时进行复原。不过,AOF 日志正好相同,它是写后日志,“写后”的意思是 Redis 是先执行命令,把数据写入内存,而后才记录日志。

why?

为了防止额定的查看开销,Redis 在向 AOF 外面记录日志的时候,并不会先去对这些命令进行语法查看。所以,如果先记日志再执行命令的话,日志中就有可能记录了谬误的命令,Redis 在应用日志复原数据时,就可能会出错。

而写后日志这种形式,就是先让零碎执行命令,只有命令能执行胜利,才会被记录到日志中,否则,零碎就会间接向客户端报错。所以,Redis 应用写后日志这一形式的一大益处是,能够避免出现记录谬误命令的状况。

除此之外,AOF 还有一个益处:它是在命令执行后才记录日志,所以不会阻塞以后的写操作

不过,AOF 也有两个潜在的危险。

首先,如果刚执行完一个命令,还没有来得及记日志就宕机了,那么这个命令和相应的数据就有失落的危险。如果此时 Redis 是用作缓存,还能够从后端数据库从新读入数据进行复原,然而,如果 Redis 是间接用作数据库的话,此时,因为命令没有记入日志,所以就无奈用日志进行复原了。

其次,AOF 尽管防止了对以后命令的阻塞,但可能会给下一个操作带来阻塞危险。这是因为,AOF 日志也是在主线程中执行的,如果在把日志文件写入磁盘时,磁盘写压力大,就会导致写盘很慢,进而导致后续的操作也无奈执行了。

混合长久化

在过来,Redis 用户通常会因为 RDB 长久化和 AOF 长久化之间不同的优缺点而陷入两难的抉择当中:

  • RDB 长久化可能疾速地贮存和复原数据,然而在服务器停机时可能会失落大量数据。
  • AOF 长久化可能无效地进步数据的安全性,然而在贮存和复原数据方面却要消耗大量的工夫。

为了让用户可能同时领有上述两种长久化的长处,Redis 4.0 推出了一个“鱼和熊掌兼得”的长久化计划 —— RDB-AOF 混合长久化:这种长久化可能通过 AOF 重写操作创立出一个同时蕴含 RDB 数据和 AOF 数据的 AOF 文件,其中 RDB 数据位于 AOF 文件的结尾,它们贮存了服务器开始执行重写操作时的数据库状态。至于那些在重写操作执行之后执行的 Redis 命令,则会持续以 AOF 格局追加到 AOF 文件的开端,也即是 RDB 数据之后

说就是说开启混合长久化之后,AOF 文件中的内容:前半部分是二进制的 RDB 内容,前面跟着 AOF 减少的数据,AOF 位于 2 次 RDB 之间。格局相似上面这样:

(二进制)RDB
 AOF(二进制)RDB

在目前版本中,RDB-AOF 混合长久化性能默认是处于敞开状态的,为了启用该性能,用户不仅须要开启 AOF 长久化性能,还须要将 aof-use-rdb-preamble 选项的值设置为 true。

appendonly yes
aof-use-rdb-preamble yes

如何抉择适合的长久化形式

当你想抉择适宜你的应用程序的长久化形式时,你须要思考以下几个因素:

  1. 数据的实时性和一致性:如果您对数据的实时性和一致性有很高的要求,则 AOF 可能是更好的抉择。
  2. 如果您对数据的实时性和一致性要求不太高,并且心愿能疾速地加载数据并缩小磁盘空间的应用,那么 RDB 就可能更适宜您的应用程序。因为 RDB 文件是二进制格局的,所以它很紧凑,并且在 Redis 重启时能够迅速地加载数据。
  3. Redis 的性能需求:如果您对 Redis 的性能有很高的要求,那么敞开长久化性能也是一个抉择。因为长久化性能可能会影响 Redis 的性能,然而个别不倡议这么做。

总结

通过本文的介绍,咱们理解了 Redis 长久化的两种形式,RDB 和 AOF,它们都有本人独特的劣势,咱们应该依据我的项目大小、数据量和业务需要制订不同的长久化策略。


本篇文章就到这里,感激浏览,如果本篇博客有任何谬误和倡议,欢送给我留言斧正。文章继续更新,能够关注公众号第一工夫浏览。

退出移动版