什么是Redis?图文并茂深入了解Redis高性能高可用的秘密
Redis 是什么?
Redis(远程字典服务)是一个开源键值数据库服务器。 Redis 更准确地描述为数据结构服务器。 Redis的这一特殊功能使其深受开发人员的欢迎。
Redis 并不是通过迭代或排序来处理数据,而是从一开始就按照数据结构来组织。早期,它的使用方式与 Memcached 类似,但随着 Redis 的发展,它变得适用于许多其他用例,包括发布-订阅机制、流式传输和队列。
基本上,Redis 是一个内存数据库,用作另一个“真实”数据库(例如 MySQL 或 PostgreSQL)前面的缓存,以提高应用程序性能。通过利用高内存访问率来减少核心应用程序数据库的负载,例如:
- 很少更改和频繁请求的数据
- 不太关键的任务和经常更改的数据
上述数据示例可能包括会话或数据缓存、排行榜或仪表板的聚合分析。
然而,Redis 为许多用例提供了足够的保证,可以用作成熟的主数据库。与 Redis 插件及其各种高可用性 (HA) 设置相结合,Redis 作为数据库对于某些场景和工作负载变得非常有用。
另一个重要的方面是Redis模糊了缓存和数据存储之间的界限。在这里,重要的是要了解内存中的数据的读取和操作速度比使用 SSD 或 HDD 进行存储的传统数据库要快得多。 †
这里是两个缓存之间当前服务的细分。
Memcached | Redis | |||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
亚毫秒级延迟 | 是 | 是 | ||||||||||||||||||||||||
是 | ||||||||||||||||||||||||||
是❙♹Easi开发者使用 是 | ||||||||||||||||||||||||||
数据分区 | 是 | 是的。是的,也可以将数据持久化到磁盘,在持久化刚提出的时候,Redis使用快照,通过异步复制数据到内存来实现持久化。不幸的是,这种机制的缺点是快照之间可能会丢失数据。 Redis 自 2009 年诞生以来已经成熟了很多。我们涵盖了 Redis 的大部分架构和拓扑,因此您可以将 Redis 添加到您的存储系统库中。 Redis 架构在我们开始讨论 Redis 的内部结构之前,我们先讨论一下各种 Redis 部署及其权衡。我们将主要关注以下设置:
D 并根据哪个设置确定要使用哪个设置。 单个 Redis 实例 单个 Redis 实例是部署 Redis 的最简单方法。它允许用户设置和运行小型实例,帮助他们快速增长和加速他们的服务。然而,这种安装并非没有缺点。例如,如果此实例发生故障或变得不可用,则对 Redis 的所有客户端调用都将失败,从而降低整体系统性能和速度。 如果您有足够的内存和服务器资源,此实例会非常高效。主要用于缓存的场景可以通过最少的设置获得显着的性能提升。有了足够的系统资源,您可以在运行应用程序的同一台计算机上安装此 Redis 服务。 在系统内处理数据时,了解一些Redis概念是至关重要的。发送到 Redis 的命令首先在内存中进行处理。然后,如果在这些实例上设置了持久性,就会每隔一段时间就会有一个 fork 进程,生成数据持久性 RDB(Redis 数据的非常紧凑的时间点表示)快照或 AOF(仅附加文件)。 。 这两个进程使Redis能够提供长期存储,支持不同的复制策略,并实现更复杂的拓扑。如果 Redis 未配置为保存数据,则数据将在重新启动或故障转移时丢失。如果重启时启用持久性,则 RDB 快照或 AOF 中的所有数据都将加载回内存,并且实例将能够支持新的客户端请求。 接下来,让我们看看您可以使用的一些分布式 Redis 设置。 Redis 高可用性 Redis 的另一种流行设置是主从部署方法,其中从部署与主部署保持数据同步。当数据写入Master时,它会将这些命令的副本发送到Slave部署客户端的输出缓冲区以实现数据同步。一个安装可以有一个或多个实例。这些实例可以帮助扩展 Redis 读取操作,或者在主服务器丢失时提供故障转移。 我们已经进入了一个分布式系统,所以这个拓扑中必须考虑到很多新的东西。以前很简单的事情现在变得复杂了。 Redis 复制 Redis 的每个主副本都有一个复制 ID 和偏移量。这两条数据对于确定副本何时可以继续复制过程或者是否需要执行完全同步至关重要。对于主 Redis 部署上发生的每个操作,此偏移量都会递增。 具体来说,如果 Redis 副本仅落后主服务器几个偏移量,它将接收来自主服务器的剩余命令,并在其数据集上重播它们,直到同步完成。如果两个实例无法就复制 ID 达成一致,或者主实例不知道偏移量,则副本会请求完全同步。此时,主服务器创建一个新的 RDB 快照并将其发送到副本。 在传输之间,主节点会缓冲快照剪切和当前偏移之间的任何中间更新指令,以便在快照同步后将它们发送到副本。完成后,复制可以照常继续。 如果实例具有相同的复制ID和偏移量,那么数据是完全相同的。现在您可能想知道为什么需要复制 ID。当您将 Redis 实例提升为主实例或从头开始将其重新启动为主实例时,它会获得一个新的复制 ID。 这是为了确定新升级的副本是从哪个先前的主节点复制的。这允许您在新主节点记住其旧复制 ID 时执行部分同步(与其他副本节点)。 例如,两个实例(主实例和从实例)具有相同的复制 ID,但偏移量相差几百个命令,这意味着如果您在实例上重播这些偏移量之后的命令,它们将具有相同的数据集。现在,如果复制ID完全不同,并且我们不知道新降级(或重新连接)的从节点的先前复制ID(它没有共同的祖先)。我们需要执行昂贵的完全同步。 如果我们知道之前的复制ID,我们就可以推断出如何同步数据,因为我们可以推断出它们共享的共同祖先,并且偏移量对于部分同步来说再次有意义。 Redis Sentinel Sentinel 是一个分布式系统。与任何分布式系统一样,Sentinel 有许多优点和缺点。 Sentinel 的设计是为了让一组 Sentinel 进程协调状态,从而为 Redis 提供高可用性。毕竟,您不希望您的故障安全系统出现单点故障。 哨兵负责一些事情。首先,它确保当前主从实例正常工作并响应。这是必需的,以便 Sentinel(以及其他 Sentinel 进程)可以在主节点和/或从节点丢失时发出警报并采取行动。其次,它在服务发现中发挥作用,就像Zookeeper和Consul在其他系统中所做的那样。因此,当新客户端尝试写入 Redis 时,Sentinel 会告诉客户端当前的主实例是什么。因此, Sentinel 会持续监控可用性并将此信息发送给客户端,以便他们在出现故障时能够做出反应。 其任务如下:
以这种方式使用Redis Sentinel可以实现错误检测。此检测涉及多个保护进程,一致认为当前主机不再可用。这个达成一致的过程称为法定人数。这提高了鲁棒性并防止一台机器行为不当并导致主 Redis 节点无法访问。 这种设置并非没有缺点,因此这里有一些使用 Redis Sentinel 的建议和最佳实践。 安装Redis Sentinel有多种方法。老实说,为了提出任何有意义的建议,我需要有关您的系统的更多背景信息。作为一般准则,我建议在每个应用程序服务器旁边运行一个看门狗(如果可能),这样您就不必考虑看门狗与实际使用 Redis 的客户端之间的网络可用性差异。 您可以使用 Redis 实例运行 Sentinel,甚至可以在独立节点上运行 Sentinel,但处理方式不同,这使事情变得更加复杂。我建议至少运行三个节点并至少有两个法定人数。这是一个简单的图表,详细说明了集群中的服务器数量以及相关的仲裁和可容忍的可持续故障。
让我们想一下这样的设置可能会出现什么问题。如果你运行这个系统足够长的时间,你就会遇到所有这些。
无法保证持久性,特别是因为磁盘上的持久性(见下文)是异步的。另一个令人不安的问题出现了,当客户端发现新的主数据库时,写入未知的主数据库会损失多少? Redis 建议在创建新连接时轮询新的主服务器。根据您的系统配置,这可能意味着大量数据丢失。 强制主服务器将写入操作复制到至少一个副本可以通过多种方式减轻损害。请注意,所有 Redis 复制都是异步的,这有其权衡。因此,您必须独立跟踪确认,如果至少一个副本未确认它们,则主服务器将停止接受写入。 Redis 集群 很多人一定想知道,当您无法将所有数据存储在机器内存中时会发生什么。目前,AWS 在线列出的单个服务器中可用的最大 RAM 为 24TIB。当然,这很多,但在某些系统中甚至不足以用于缓存层。 Redis Cluster 实现了 Redis 的水平扩展。 首先,让我们先了解一些术语;当我们决定使用Redis Cluster之后,我们决定将我们存储的数据分布在几台机器上,这就是所谓的共享。因此,集群中的每个 Redis 实例都被视为整个数据的一个分片。 这就提出了一个新问题。如果我们将一个键推送到集群,我们如何知道哪个 Redis 实例(分片)包含该数据?有多种方法可以做到这一点,但 Redis Cluster 使用算法分区。 为了查找属于给定键的分片,它会提取该键与分片总数的模。然后,借助确定性哈希函数,即给定的密钥将始终映射到同一个分片,我们可以推断给定的密钥将来将在哪里读取。 如果我们稍后想向系统添加新分片会怎样?这个过程称为重新分片。 假设 'foo' 键之前映射到分片 0,那么在引入新分片后,它可能会映射到分片 5。然而,如果我们需要快速扩展系统,移动数据以访问新的分片映射将会很慢且不切实际。这也对Redis集群的可用性产生了不利影响。 Redis Cluster针对这个问题设计了一个解决方案,叫做Hashslot,所有的数据都映射到它上面。有 16K 个哈希位置。这提供了一种在集群中分发数据的合理方法,当添加新分片时,我们只需在系统之间移动哈希槽即可。这只需要我们将 hashlot 从一个分片移动到另一个分片,并简化向集群添加新主实例的过程。 这是在没有停机的情况下实现的,并且对性能的影响最小。让我们通过一个例子来谈谈。
因此,为了映射“foo”,我们修改密钥的确定性哈希(itfo 哈希)。哈希槽(16K),产生M2的映射。现在假设我们添加一个新的 M3 实例。新映射将是:
现在 M1 中映射到 M2 的所有键都需要移动。但是属于散列槽的各个键的散列不需要移动,因为它们已经在散列槽中分区。因此,这种程度的误导解决了算法分区的重新分配问题。 Gossiping 协议 Redis 集群使用 gossip 来确定整个集群的状态。上图中,有 3 个 M 节点和 3 个 S 节点。所有这些节点都在不断地进行通信,以找出哪些分片可用并准备好服务请求。 如果足够多的分片同意 M1 无响应,它们可以决定提升 M1 的 S1 副本来主控它,以保持集群健康。启动操作所需的节点数量是可配置的,并且必须正确执行。如果你做错了并且在分区两侧相等时未能打破平局,则可能会导致集群分裂。这种现象称为脑裂。作为一般规则,您应该拥有奇数个主设备和两个副本,以实现最稳健的设置。 Redis 持久化模型当使用 Redis 存储任何类型的数据并需要安全存储时,了解 Redis 的工作原理非常重要。在许多情况下,丢失 Redis 中存储的数据并不是世界末日。将其用作缓存或支持实时分析的地方,即使发生数据丢失也不是世界末日。在其他情况下,我们希望提供有关数据持久性和恢复的某些保证。![]() 无持久性 无持久性:如果您愿意,可以完全禁用持久性。这是运行 Redis 的最快方式,但无法保证持久性。 RDB 文件 RDB(Redis 数据库):RDB 持久性以指定的时间间隔拍摄数据集的快照。 这种机制的主要缺点是快照之间数据会丢失。此外,这种存储机制依赖于分叉主进程,这可能会导致在处理较大数据集中的请求时出现短暂延迟。然而,RDB 文件加载到内存中的速度比 AOF 快得多。 AOF AOF (Append File Only):AOF 持续记录服务器收到的每一个写操作。当服务器开始重建原始数据集时,这些操作会再次执行。 这种持久性方法比 RDB 快照提供了更好的持久性,因为它是仅追加文件。操作发生时会缓冲在日志中,但尚未持久化。该日志与实际运行的命令一致,因此可以在需要时重播。 然后,如果可能的话,使用 fsync 将其转储到磁盘(如果此运行是可配置的),并将其保留在磁盘上。缺点是格式不紧凑,并且比 RDB 文件使用更多的磁盘空间。 为什么不双向呢? RDB + AOF:AOF 和 RDB 可以组合在同一个 Redis 实例中。如果你愿意的话,交易速度是对持久性的权衡。我认为这是一种可以接受的设置 Redis 的方式。重新启动时,请注意,如果两者都启用,Redis 将使用 AOF 来重建数据,因为它是最完整的。 Forking 现在我们了解了持久化的类型,让我们讨论如何在像Redis这样的单线程应用程序中实际实现它。 在我看来,Redis 最酷的部分是它使用分支和写时复制来有效帮助数据持久化的方式。 分叉是操作系统通过创建自身副本来创建新进程的一种方式。这将为您提供一个新的进程 ID 以及一些其他信息和处理程序,以便新分叉的进程(子进程)可以与原始进程父进程进行通信。 现在事情变得有趣了。 Redis是一个占用大量内存的进程,那么如何在不耗尽内存的情况下进行复制呢? 当fork一个进程时,父进程和子进程共享内存,Redis会在子进程中启动快照(Redis)进程。这是通过使用称为复制写入的内存共享技术来实现的,该技术在创建分叉时传递对内存的引用。如果子进程仍在磁盘上时没有发生任何更改,则不会进行新的分配。 如果发生更改,内核会跟踪每个页面的链接,如果页面包含多个更改,则这些更改将写入新页面。子进程完全不知道变化并且有一致的内存快照。因此,我们可以非常快速有效地拍摄潜在千兆字节内存的快照,同时仅使用一小部分内存! |
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。