Code前端首页关于Code前端联系我们

什么是Redis?连傻瓜都能看懂的图解

terry 2年前 (2023-09-26) 阅读数 52 #后端开发

1

Redis是什么?

Redis(远程字典服务)是一个开源键值数据库服务器。

Redis 更准确地描述为数据结构服务器。 Redis的这一特殊属性使其深受开发者的青睐。

什么是 Redis?傻瓜都能看懂的图解

Redis 不会通过迭代或排序来处理数据,而是从一开始就按照数据结构进行组织。早期,它被广泛用作 Memcached,但随着 Redis 的改进,它在许多其他用例中变得可行,包括发布-订阅机制、流式传输和队列。

什么是 Redis?傻瓜都能看懂的图解

基本上,Redis 是一个内存数据库,用作另一个“真实”数据库(例如 MySQL 或 PostgreSQL)之前的缓存,以提高应用程序性能。它通过利用高内存访问速度来减少核心应用程序数据库的负载,例如:

  • 很少更改且经常请求的数据
  • 任务关键性较低且经常更改的数据

上述数据示例可以是会话或包括数据缓存,以及排行榜或仪表板的聚合分析。

什么是 Redis?傻瓜都能看懂的图解

然而,对于许多用例场景,Redis 提供了足够的保证来用作成熟的主数据库。与 Redis 插件及其各种高可用性 (HA) 设置相结合,Redis 作为数据库对于某些场景和工作负载变得非常有用。

另一个重要的方面是 Redis 模糊了缓存和数据存储之间的界限。这里需要了解的重要一点是,内存中数据的读取和操作速度比使用 SSD 或 HDD 进行存储的传统数据库要快得多。

什么是 Redis?傻瓜都能看懂的图解

一开始,Redis 最常被拿来与 Memcached 进行比较,后者当时缺乏持久性。

这是两个缓存之间的当前功能细分。

MemcachedRedis
亚毫秒级延迟
开发人员易用性❝是❝数据分区
支持多种编程语言,有些配置是为了将数据持久化到磁盘,当持久化刚被引入时,Redis使用快照通过在内存中异步复制数据来实现持久化。不幸的是,这种机制的缺点是快照之间可能会丢失数据。

Redis 自 2009 年诞生以来已经成熟了很多。我们将介绍其大部分架构和拓扑,以便您可以将 Redis 添加到您的数据存储系统库中。

2

Redis 架构

在我们开始讨论 Redis 内部结构及其 Redis 部署之前。

我们主要关注以下设置:

  • 单个 Redis 实例
  • Redis 高可用性
  • Redis Sentinel
    根据您的用例和规模进行决定。

    单个 Redis 实例

    什么是 Redis?傻瓜都能看懂的图解

    Redis 实例是安装 Redis 最简单的方法。它允许用户设置和运行小型实例,帮助他们快速增长并加速他们的服务。然而,这种安装并非没有缺点。例如,如果此实例发生故障或变得不可用,则所有客户端对 Redis 的调用都将失败,从而降低系统的整体性能和速度。

    只要有足够的内存和服务器资源,这个实例就可以非常强大。主要用于缓存的场景可以通过最少的设置获得显着的性能提升。如果有足够的系统资源,您可以将此 Redis 服务部署在运行应用程序的同一台计算机上。

    在管理系统中的数据时,了解一些 Redis 概念至关重要。发送到 Redis 的命令首先在内存中进行处理。然后,当在这些实例上设置持久性时,在一个间隔内有一个 fork 进程,生成数据持久性 RDB(Redis 数据的非常紧凑的时间点表示)快照或 AOF(仅添加文件)。

    这两个进程让Redis能够拥有长期存储,支持多种复制策略,并能够实现更复杂的拓扑。如果 Redis 未配置为保存数据,则数据将在重新启动或故障转移时丢失。如果启用重启时持久性,它会将 RDB 快照或 AOF 中的所有数据加载回内存,然后实例可以支持新的客户端请求。

    话虽如此,让我们看看一些可以使用的更多分布式 Redis 设置。

    Redis 高可用性

    什么是 Redis?傻瓜都能看懂的图解

    Redis 的另一种流行框架是主从部署方法,其中从部署与主部署保持数据同步。当数据写入主实例时,它会将这些命令的副本发送到从部署客户端输出缓冲区以实现数据同步。部署中可以有一个或多个实例。这些实例可以帮助扩展 Redis 读取操作或在 main 丢失时进行故障转移。

    我们现在进入了一个分布式系统,所以这个拓扑中有很多新的东西需要考虑。以前简单的事情现在变得复杂了。

    Redis 复制

    Redis 的每个主实例都有一个复制 ID 和一个偏移量。这两条数据对于确定副本可以继续其复制过程或确定是否需要执行完全同步至关重要。对于主 Redis 部署上发生的每个操作,此偏移量都会递增。

    更具体地说,如果 Redis 副本实例仅落后主实例几个偏移量,它会从主实例接收剩余命令,然后在其数据集上重播它们,直到同步完成。如果两个实例在复制 ID 上不一致,或者主实例不知道偏移量,则副本会请求完全同步。此时,主服务器创建一个新的 RDB 快照并将其发送到副本。

    在此传输期间,主实例会缓冲快照截止和当前偏移之间的所有中间更新指令,以便在快照同步后将它们发送到副本实例。一旦完成,复制就可以正常进行。

    如果实例具有相同的复制 ID 和偏移量,则它们具有完全相同的数据。现在您可能想知道为什么需要复制 ID。当 Redis 实例升级为主实例或从头开始重新启动为主实例时,它会获得一个新的复制 ID。

    这用于确定此新升级的副本实例是从哪个先前的主实例复制的。这允许执行部分同步(与其他副本节点),因为新主节点会记住其旧的复制 ID。

    例如,两个实例(主实例和从实例)具有相同的复制 ID,但偏移量相差数百个命令,这意味着当在实例上的这些偏移量之后重放命令时,它们将具有相同的数据集。现在,如果复制ID完全不同,并且我们不知道新降级(或重新加入)从节点的先前复制ID(没有共同祖先)。我们将不得不进行昂贵的完全同步。

    相反,如果我们知道之前的复制 ID,我们就可以推断出数据是如何同步的,因为我们可以推断出它们共享的共同祖先,并且偏移量对于部分同步来说再次有意义。

    Redis Sentinel

    什么是 Redis?傻瓜都能看懂的图解

    Sentinel 是一个分布式系统。与所有分布式系统一样,Sentinel 有几个优点和缺点。 Sentinel 的设计是为了让多个 Sentinel 进程协同工作来协调状态,从而为 Redis 提供高可用性。毕竟,您不希望保护您免受故障影响的系统有自己的单点故障。

    Sentinel 负责几件事。首先,它确保当前主从实例正常工作并响应。这是必要的,以便 Sentinel(以及其他 Sentinel 进程)可以在主节点和/或从节点丢失时发出警报并采取行动。其次,它起到服务发现的作用,就像其他系统中的Zookeeper、Consul一样。因此,当新客户端尝试写入 Redis 时,Sentinel 会告诉客户端当前的主实例是什么。

    因此,Sentinel 会持续监控可用性并将此信息发送给客户端,以便他们在出现故障时能够做出反应。

    以下是他们的职责:

    • 监控 - 确保主实例和从实例按预期工作。
    • 通知 - 通知系统管理员有关 Redis 实例中的事件。
    • 故障转移管理 - 如果主实例不可用并且足够的(仲裁)节点同意这一点,哨兵节点可以启动故障转移。
    • 配置管理 - Sentinel 节点还充当当前主 Redis 实例的发现服务。

    以这种方式使用 Redis Sentinel 可以实现错误检测。此检测涉及多个哨兵进程一致认为当前主实例不再可用。这个共识过程称为法定人数。这提高了鲁棒性并防止机器行为不当导致主 Redis 节点无法访问。

    此设置并非没有缺点,因此我们将介绍使用 Redis Sentinel 时的一些建议和最佳实践。

    您可以通过多种方式部署Redis Sentinel。坦率地说,为了提出有意义的建议,我需要更多有关您的系统的背景信息。作为一般指南,我建议在每个应用程序服务器旁边运行 Sentinel 节点(如果可能),这样您就不必考虑 Sentinel 节点与使用 Redis 的客户端之间的网络可达性差异。

    您可以使用 Redis 实例运行 Sentinel,甚至可以在独立节点上运行 Sentinel,但处理方式不同,这使事情变得更加复杂。我建议至少运行三个节点,并且法定人数至少为两个。这是一个简单的图表,详细说明了集群中的服务器数量以及相关的仲裁和可以容忍的持续故障。

    服务器数量法定人数容忍的错误数量
    110
    220
    321
    431
    532
    642
    743

    这会因系统而异,但总体思路保持不变。

    让我们花点时间思考一下这样的设置可能会出现什么问题。如果你运行这个系统足够长的时间,你就会遇到所有这些。

    1. 如果哨兵节点超过法定人数怎么办?
    2. 如果网络分裂使旧的主实例成为少数怎么办?这些著作会发生什么? (剧透:系统完全恢复后就会丢失)
    3. 如果哨兵节点和客户端节点(应用节点)的网络拓扑错位会怎样?

    没有持久性保证,特别是因为持久化到磁盘的操作(见下文)是异步的。当客户发现新小学时,还有一个恼人的问题,写给一所不知名的小学,我们会损失多少? Redis 建议在建立新连接时查询新的 master。根据系统配置,这可能意味着大量数据丢失。

    如果强制主实例将写入操作复制到至少一个副本实例,有多种方法可以减少损坏程度。请记住,所有 Redis 复制都是异步的,这有其缺点。因此,它必须独立跟踪确认,如果至少一个副本实例没有确认,主实例将停止接受写入。

    Redis 集群

    什么是 Redis?傻瓜都能看懂的图解

    我相信很多人都想过如果你不能将所有数据存储在一台机器的内存中会发生什么。目前,单台服务器可用的最大 RAM 为 24TIB,目前 AWS 在线列出了该值。当然,这很多,但对于某些系统来说这还不够,即使对于缓存层也是如此。

    Redis Cluster 允许 Redis 水平扩展。

    首先,让我们先了解一些术语;一旦我们决定使用Redis集群,我们就决定将存储的数据分布在不同的机器上,这称为分片。因此集群中的每个Redis实例都被视为整个数据的一个切片。

    这就提出了一个新问题。当我们将一个键推送到集群时,我们如何知道哪个 Redis 实例(分片)保存了数据?有多种方法可以做到这一点,但 Redis Cluster 使用算法分片。

    为了查找给定密钥的分片,我们将密钥对分片总数取模。然后,使用确定性哈希函数,这意味着某个键总是映射到同一个分片,我们可以推断将来将在哪里读取特定键。

    如果我们稍后想向系统添加新剪辑会怎样?这个过程称为重新分片。

    假设键 'foo' 之前映射到分片 0,那么在插入新分片后,它可以映射到分片 5。然而,如果我们需要快速扩展系统,移动数据以实现新的分片映射将是缓慢且不切实际的。它还会对 Redis 集群的可用性产生负面影响。

    Redis Cluster 针对这个问题设计了一个解决方案,称为哈希槽,所有数据都映射到它上面。有16K个哈希槽。这为我们提供了一种在集群中传播数据的合理方法,当我们添加新分片时,我们只需在系统之间移动哈希槽即可。通过这样做,我们只需将 hashlot 从一个分片移动到另一个分片,并简化向集群添加新主实例的过程。

    这可以在不中断且对性能影响最小的情况下实现。让我们通过一个例子来谈谈。

    • M1 包含从 0 到 8191 的哈希位置。
    • M2 包含从 8192 到 16383 的哈希位置。哈希位置(16K),从而得到 M2 的映射。现在假设我们添加一个新实例 M3。新映射将为:
      • M1 包含从 0 到 5460 的哈希槽。
      • M2 包含从 5461 到 10922 的哈希槽。
      • M3 包含从 10923 到 16383 的哈希槽。

      现在必须移动映射到 M2 的 M1 中映射哈希槽的所有键。但是散列槽的各个键的散列不需要移动,因为它们已经被划分到散列槽中。因此,这种程度的误导解决了算法分片的重新分片问题。

      Gossiping Protocol

      Redis 集群使用 gossip 来确定整个集群的健康状况。在上图中,我们有 3 M 个顶点和 3 S 个顶点。所有这些节点都在不断地进行通信,以了解哪些分片可用并准备好服务请求。

      如果有足够多的节点认为 M1 没有响应,它们可以决定控制 M1 的副本 S1 以保持集群健康。触发该操作所需的节点数量是可配置的,并且必须正确执行。当分区两侧相等时,如果做错了并且未能打破平局,可能会导致集群分裂。这种现象称为裂脑。作为一般规则,您应该拥有奇数个主设备和两个副本,以实现最稳健的设置。

      3

      Redis 持久化模型

      当我们使用 Redis 时,如果存储此类数据很重要,我们将使用持久化来存储此类数据。 。在许多用例中,如果丢失了存储在 Redis 中的数据,这并不是世界末日。将其用作缓存或支持实时分析的地方,即使发生数据丢失也不是世界末日。

      在其他场景中,我们希望在数据持续时间和恢复方面有一些保证。

      什么是 Redis?傻瓜都能看懂的图解

      无持久性

      无持久性:如果需要,您可以完全关闭持久性。这是运行 Redis 的最快方式,并且没有持久性保证。

      RDB 文件

      RDB(Redis 数据库):RDB 持久性以指定的时间间隔拍摄数据集的时间点快照。

      这种机制的主要缺点是快照之间数据会丢失。此外,这种存储机制依赖于主进程的分支,这可能会导致在处理较大数据集中的请求时出现短暂延迟。话虽这么说,RDB 文件在内存中的加载速度比 AOF 快得多。

      AOF

      AOF(仅下载文件):AOF 持久记录从服务器接收到的每个写操作。当服务器开始重建原始数据集时,这些操作会再次执行。

      这种持久化方法比 RDB 快照提供更高的持久性,因为它是单个文件。当操作发生时,我们将它们缓冲在日志中,但它们尚未持久化。该日志与我们当前运行的命令一致,因此如果需要可以重播。

      然后,如果可能的话,我们使用 fsync 将其刷新到磁盘(如果此运行是可配置的),并且它将持续存在。缺点是格式不紧凑,并且比 RDB 文件使用更多的磁盘。

      为什么不两种方式呢?

      RDB + AOF:AOF 和 RDB 可以组合在同一个 Redis 实例中。如果你愿意的话,用速度换取持久性是一种妥协。我认为这是一种可以接受的设置 Redis 的方式。如果重新启动,请记住,如果两者都启用,Redis 将使用 AOF 来重建数据,因为它是最完整的。

      Forking

      现在我们了解了持久化的类型,让我们讨论如何在像Redis这样的单线程应用程序中实际实现它。

      什么是 Redis?傻瓜都能看懂的图解

      在我看来,Redis 最酷的部分是它如何使用分叉和写时复制来有效地促进数据持久化。

      分叉是操作系统通过创建自身副本来创建新进程的一种方式。这样,你就获得了一个新的进程ID和一些其他信息以及进程,以便新的fork进程(子进程)可以与原始进程父进程进行通信。

      现在事情变得有趣了。 Redis是一个分配大量内存的进程,那么它如何在不耗尽内存的情况下进行复制呢?

      当启动一个进程时,父进程和子进程共享内存,并且在子进程中,Redis启动快照(Redis)进程。这是通过称为写时复制的内存共享技术来实现的,该技术在创建分叉时传递对内存的引用。如果子进程保留在磁盘上时没有发生任何更改,则不会进行新的分配。

      如果发生更改,内核会保留对每个页面的引用,如果页面有更多更改,则更改会写入新页面。子进程完全不知道变化并且有一致的内存快照。因此,我们可以非常快速有效地获取潜在千兆字节内存的时间点快照,同时仅使用一小部分内存!

      来源:Geek Reborn

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门