查看“Redis:集群教程”的源代码
←
Redis:集群教程
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[category:Redis]] == 关于 == 参考:[https://redis.io/topics/cluster-tutorial https://redis.io/topics/cluster-tutorial] 以下包含了有关如何设置集群、测试和操作集群的说明,而不涉及Redis 集群规范中涵盖的细节。 == Redis 集群 == Redis Cluster 提供了一种运行 Redis 安装的方法,其中'''数据在多个 Redis 节点之间自动分片'''。 Redis Cluster 还在'''分区期间'''提供了'''一定程度的可用性''',这实际上是在某些节点出现故障或无法通信时继续操作的能力。但是,如果发生较大的故障(例如,当大多数主节点不可用时),集群将停止运行。 那么在实践中,您从 Redis Cluster 中得到了什么? # 在多个节点之间自动拆分数据集的能力。 # 当部分节点出现故障或无法与集群的其余部分通信时继续操作的能力。 === Redis 集群:TCP 端口 === 每个 Redis Cluster 节点都需要打开两个 TCP 连接: # 用于为客户端提供服务的普通 Redis TCP 端口(如:6379); # 以及名为“集群总线端口”(Cluster Bus Port)的第二个端口。 #* 集群总线端口:将数据端口+10000(如:16379)添加10000;或使用集群端口配置覆盖。 第二个高端口用于集群总线,即使用二进制协议的节点到节点通信通道。节点使用集群总线进行故障检测、配置更新、故障转移授权等。 客户端永远不要尝试与集群总线端口通信,而应始终与正常的 Redis 命令端口通信,但请确保'''在防火墙中打开这两个端口''',否则 Redis 集群节点将无法通信。 请注意,要使 Redis 集群正常工作,您需要为每个节点: # 用于与客户端通信的普通客户端通信端口(通常为6379)将向需要到达群集的所有客户端以及所有其他群集节点(使用客户端端口进行密钥迁移)开放。 # 集群总线端口必须可以从所有其他群集节点访问。 * 如果您不同时打开两个 TCP 端口,您的集群将无法按预期工作。 集群总线使用一种不同的二进制协议进行节点到节点的数据交换,这种协议更适合于使用较少的带宽和处理时间在节点之间交换信息。 === Redis集群 和 Docker === 目前,Redis Cluster 不支持 NATED 环境,也不支持 IP 地址或 TCP 端口重新映射的一般环境。 Docker 使用了一种称为'''端口映射'''的技术:运行在 Docker 容器中的程序可能会使用不同的端口公开,而该端口与程序认为正在使用的端口不同。这对于在同一服务器上同时使用相同端口运行多个容器非常有用。 为了使 Docker 与 Redis 群集兼容,您需要使用 Docker 的'''主机网络模式'''。 * 有关更多信息,请查看 Docker 文档中的“--net=host”选项。 === Redis集群:数据分片 === Redis Cluster 不使用[[http://wiki.eijux.com/%E5%85%B3%E4%BA%8E%EF%BC%9A%E4%B8%80%E8%87%B4%E6%80%A7%E5%93%88%E5%B8%8C%E7%AE%97%E6%B3%95 一致哈希(consistency hashing)]],而是一种不同形式的分片,其中每个键在概念上都是我们所称的'''散列槽'''的一部分。 Redis 集群中有 '''16384'''(2 ^ 14)个哈希槽,使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽。 Redis 集群中的'''每个节点负责哈希槽的子集'''。 : 因此,例如,您可能有一个包含3个节点的集群,其中: :# 节点 A 包含从 0 到 5500 的哈希槽。 :# 节点 B 包含从 5501 到 11000 的哈希槽。 :# 节点 C 包含从 11001 到 16383 的哈希槽。 这允许在集群中轻松地添加和删除节点。 : 例如,如果我想添加一个新节点 D,我需要将一些哈希槽从节点 A、B、C 移动到 D。类似地,如果我想从集群中删除节点 A,我可以只将由 A 提供服务的哈希槽移动到 B 和 C。当节点 A 为空时,我可以将其完全从集群中删除。 * 由于将哈希槽从一个节点移动到另一个节点不需要停止操作,因此添加和删除节点,或更改节点持有的哈希槽的百分比,不需要任何停机时间。 '''Redis Cluster 支持多个键操作,只要单个命令执行'''(或整个事务,或 Lua 脚本执行)'''中涉及的所有键都属于同一个哈希槽'''。 用户可以使用名为'''哈希标记(hash tags)'''的概念强制多个键成为同一哈希槽的一部分。 哈希标记记录在 Redis 集群规范中,其要点:如果在一个键的“'''{}'''”括号之间有一个子字符串,则'''只对字符串括号“{}”内的内容进行哈希处理'''。 : 例如“''this{foo}key''”和“''another{foo}key''”保证位于同一个哈希槽中,并且可以在具有多个键作为参数的命令中一起使用。 === Redis集群:主从模型 === 为了在主节点子集出现故障或无法与大多数节点通信时保持可用,Redis Cluster 使用主从模型,其中每个哈希槽具有1个(主节点本身)到 N 个副本(N-1 个附加副本节点)。 在具有节点 A、B、C 的示例集群中,如果节点 B 出现故障,集群将无法继续,因为我们不再能够提供 5501-11000 范围内的哈希槽。 但是,在创建集群时(或稍后),我们会向每个主节点添加一个副本节点,这样最终的集群就由:主节点 A、B、C,从节点 A1、B1、C1 组成。 这样,如果节点 B 出现故障,系统就能够继续:节点 B1 复制 B,如果 B 失败,集群将提升节点 B1 作为新的主节点,并将继续正常运行。 * 但是,请注意,如果节点 B 和 B1 同时出现故障,Redis 群集将无法继续运行。 === Redis集群:一致性保证 === '''Redis Cluster 无法保证强一致性(strong consistency)'''。实际上,这意味着在某些情况下,Redis Cluster 可能会丢失系统已向客户端确认的写入。 Redis Cluster 会丢失写入的第一个原因是因为它使用'''异步复制'''。 : 这意味着在写入期间会发生以下情况: :# 您的客户端写入主 B。 :# 主 B 向您的客户端回复 OK。 :# 主 B 将写入传播到其副本 B1、B2 和 B3。 : 如您所见,B 在回复客户端之前不会等待来自 B1、B2、B3 的确认,因为这对 Redis 来说是一个令人望而却步的延迟惩罚,因此如果您的客户端写入某些内容,B 会确认写入,但如果在将写入发送到其副本之前崩溃,其中一个副本(未收到写入)可以提升为主,那么写入的内容将永远丢失。 :* 然而,这种情况不太可能发生,因为Redis几乎同时向客户机发送回复和复制到副本的命令,因此丢失数据的窗口很小。但是,很难触发的事实并不意味着不可能触发,因此这不会改变 Redis cluster 提供的一致性保证。 这与大多数配置为“每秒将数据刷新到磁盘”的数据库发生的情况非常相似。 您可以通过强制数据库在回复客户端之前将数据刷新到磁盘来提高一致性,但这通常会导致效率过低。在 Redis Cluster 的情况下,这相当于同步复制。 基本上,需要在性能和一致性之间进行权衡。 Redis 集群在绝对需要时支持同步写入,通过 '''WAIT''' 命令实现。这使得丢失写入的可能性大大降低。但是,请注意,即使使用同步复制,Redis Cluster 也没有实现强一致性:在更复杂的故障场景下,始终有可能将无法接收写入的副本选为 master。 还有一个值得注意的场景是 Redis 集群将丢失写入,这发生在'''网络分裂'''期间,客户端与少数实例(至少包括一个主实例)隔离。 : 以我们的 6 个节点集群为例,它由 A、B、C、A1、B1、C1 组成,有 3 个主节点和 3 个副本节点。还有一个客户端,我们将其称为 Z1。 : 分区发生后,可能在分区的一侧有 A、C、A1、B1、C1,而在另一侧有 B 和 Z1。 : Z1 仍然能够写入 B,B 将接受其写入。如果分区在很短的时间内恢复,集群将继续正常运行。但是,如果分区持续足够的时间让 B1 在分区的多数侧提升为 master,则 Z1 同时发送给 B 的写入将丢失。 :* 请注意,Z1 能够发送到 B 的写入量有一个最大窗口:如果分区的多数侧已经有足够的时间来选举一个副本作为主节点,则少数侧的每个主节点都将停止接受写入。 这个时间量是 Redis Cluster 的一个非常重要的配置指令,称为'''节点超时'''。 : 节点超时后,主节点被视为发生故障,并且可以由其副本之一替换。类似地,在节点超时后,主节点无法感知大多数其他主节点,它会进入错误状态并停止接受写入。 == Redis集群配置参数 == Redis Cluster 在 redis.conf 文件中引入的配置参数: * '''cluster-enabled <yes/no>''':如果 yes,则在特定 Redis 实例中启用 Redis Cluster 支持。否则,该实例像往常一样作为独立实例启动。 * '''cluster-config-file <filename>''':注意,尽管这个选项的名称,'''这不是一个用户可编辑的配置文件''',而是一个 Redis Cluster 节点在每次有变化时自动持久化集群配置(状态,基本上)的文件,以便能够在启动时重新读取它。 ** 该文件列出了集群中的其他节点、它们的状态、持久变量等内容。 ** 由于某些消息接收,此文件通常会被重写并刷新到磁盘上。 * '''cluster-node-timeout <milliseconds>''':节点超时值,Redis 集群节点不可用而不被视为失败的最长时间。 *: 如果主节点在指定的时间内无法访问,它将由其副本进行故障转移。 ** 该参数控制 Redis Cluster 中的其他重要事项。 ** 值得注意的是,在指定的时间内无法访问大多数主节点的每个节点都将停止接受查询。 * '''cluster-slave-validity-factor <factor>''': *# 如果设置为 0,副本将始终认为自己有效,因此无论主服务器和副本之间的链接保持断开连接的时间长短,将始终尝试故障转移主服务器。 *# 如果值为正,则将节点超时值乘以此选项提供的因子,作为最大断开时间,并且如果节点是副本,在主链接断开的时间超过指定的时间,它不会尝试启动故障转移。 *: 例如,如果节点超时设置为 5 秒且有效性因子设置为 10,则与主服务器断开连接超过 50 秒的副本将不会尝试对其主服务器进行故障转移。 ** 请注意,如果没有用于故障转移的副本,则任何非零值都可能导致 Redis 集群在主节点故障后不可用。在这种情况下,只有当原始主节点重新加入集群时,集群才会恢复可用。 * '''cluster-migration-barrier <count>''':一个 master 将保持连接的最小副本数,以便另一个副本迁移到不再被任何副本覆盖的 master。【?】 * '''cluster-require-full-coverage <yes/no>''': *# 如果将其设置为 yes(默认),如果任何节点都没有覆盖一定百分比的 key space,则集群将停止接受写入。 *# 如果该选项设置为 no,即使只能处理关于 key 子集的请求,集群仍将提供查询服务。 * '''cluster-allow-reads-when-down <yes/no>''': *# 如果设置为 no(默认),当集群被标记为失败时,Redis 集群中的节点将停止服务所有流量,无论是当节点数量无法达到主机的 quorum配置,还是没有达到全覆盖。 *#* 这可以防止从不知道集群中的变化的节点读取可能不一致的数据。 *# 可以将此选项设置为 yes 以允许在失败状态期间从节点读取,这对于希望优先考虑读取可用性但仍希望防止不一致写入的应用程序非常有用。 *#* 它也可以用于使用只有一个或两个分片的 Redis Cluster 时,因为它允许节点在主节点发生故障但无法自动故障转移时继续提供写入服务。 == 创建并使用Redis集群 == 要创建集群,我们需要做的第一件事是'''在集群模式下运行'''几个空的 Redis 实例。这基本上意味着集群不是使用普通 Redis 实例创建的,因为需要配置特殊模式,以便 Redis 实例启用特定于集群的功能和命令。 以下是最小的 Redis 群集配置文件: : '''<syntaxhighlight lang="yaml" highlight=""> port 7000 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes </syntaxhighlight>''' 如上: * 启用集群模式需要配置 cluster enabled。 * 每个实例还包含存储此节点配置的文件的路径,默认情况下为 nodes.conf。这不是一个用户可编辑的配置文件,它由 Redis 集群实例在启动时生成,并在每次需要时更新。 请注意,按预期工作的最小集群需要包含'''至少三个主节点'''。 示例:(创建六个节点的集群) : 请输入一个新目录,并创建以下目录,这些目录以我们将在任何给定目录中运行的实例的端口号命名。如下: : <syntaxhighlight lang="bash" highlight=""> mkdir cluster-test cd cluster-test mkdir 7000 7001 7002 7003 7004 7005 </syntaxhighlight> : 在从 7000 到 7005 的每个目录中创建一个 '''redis.conf''' 文件。作为配置文件的模板,只需使用上面的小示例,但请确保根据目录名称配置正确的端口号。 : 现在,将从 GitHub 不稳定分支中的最新源代码编译的 redis server 可执行文件复制到集群测试目录中,最后在您喜爱的终端应用程序中打开6个终端选项卡。 : 在每个选项卡像这样启动每个实例: : <syntaxhighlight lang="bash" highlight=""> cd 7000 ../redis-server ./redis.conf </syntaxhighlight> : 从每个实例的日志中可以看到,'''由于不存在 nodes.conf 文件,每个节点都为自己分配一个新 ID''': : <syntaxhighlight lang="bash" highlight=""> [82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1 </syntaxhighlight> :* 此特定实例将永远使用此 ID,以便实例在集群上下文中具有唯一的名称。每个节点都会记住使用此 ID 的每个其他节点,而不是通过IP或端口。IP地址和端口可能会更改,但在节点的整个生命周期内,唯一节点标识符永远不会更改。我们称这个标识符为'''节点 ID'''。 === 创建集群 === 现在已经有许多实例在运行,我们需要通过向节点写入一些有意义的配置来创建集群。 如果您使用的是 Redis 5 或更高版本,这是非常容易实现的,因为我们在 '''redis-cli''' 中嵌入的 Redis Cluster 命令行实用程序的帮助下,可以使用该实用程序创建新群集、检查或重新硬处理现有群集等。 对于 Redis 3 或 4 版本,有一个名为 '''redis-trib.rb''' 的旧工具,它非常类似。您可以在 Redis 源代码发行版的 src 目录中找到它。您需要安装 redis gem 才能运行 redis-trib。 “gem install redis” 如果您愿意,可以对 Redis 4 群集使用 Redis 5 的 redis-cli,而不会出现问题。 # 使用 '''redis-cli''' 为 Redis 5 创建群集,只需键入: #: <syntaxhighlight lang="bash" highlight=""> redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \ 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \ --cluster-replicas 1 </syntaxhighlight> #* 这里使用的命令是“create”,因为我们想要创建一个新集群。选项“--cluster replicas 1”意味着我们需要为创建的每个主服务器创建一个副本。其他参数是我要用于创建新集群的实例的地址列表。 #*: 显然,符合我们要求的唯一设置是创建一个包含3个主机和3个副本的集群。 # redis-cli 将向您建议一种配置。通过键入 '''yes''' 接受建议的配置。集群将被配置和加入,这意味着实例将被引导到相互对话中。最后,如果一切顺利,您将看到这样一条消息: #: <syntaxhighlight lang="bash" highlight=""> [OK] All 16384 slots covered </syntaxhighlight> #: 这意味着至少有一个主实例为16384个可用插槽中的每一个提供服务。 === 使用 create-cluster 脚本创建Redis群集 === 如果您不想通过如上所述手动配置和执行单个实例来创建 Redis 集群,那么有一个简单得多的系统(但您不会学到相同数量的操作细节)。 有关如何运行脚本的详细信息,请阅读此目录中的自述文件。 只需检查 Redis 发行版中的 '''utils/create-cluster''' 目录。里面有一个名为 '''create-cluster''' 的脚本(与包含它的目录同名),它是一个简单的 bash 脚本。要启动包含3个主机和3个副本的6节点群集,只需键入以下命令: <syntaxhighlight lang="bash" highlight=""> create-cluster start create-cluster create </syntaxhighlight> 当 redis-cli 实用程序希望您接受群集布局时,在步骤2中回答“是”。 您现在可以与集群交互,默认情况下,第一个节点将从端口 30001 开始。完成后,使用以下命令停止群集: <syntaxhighlight lang="bash" highlight=""> create-cluster stop </syntaxhighlight> === 使用集群 === 现阶段 Redis 集群的一个问题是缺少客户端库实现。 以下是一些我知道的实现: * redis-rb-cluster:是我(@antrez)编写的一个 Ruby 实现,作为其他语言的参考。它是一个围绕原始 redis-rb 的简单包装器,实现了与集群高效对话的最小语义。 * redis-py-cluster:redis-rb-cluster 到 Python 的端口。支持大多数 redis-py 功能。正在积极发展中。 * 流行的 Predis 支持 Redis 群集,该支持最近更新,正在积极开发中。 * 最常用的 Java 客户机 '''Jedis''' 最近增加了对 Redis Cluster 的支持。 ** 请参阅项目自述中的Jedis Cluster部分。 * StackExchange.Redis 提供了对 C# 的支持(并且可以与大多数.NET语言、VB、F#等配合使用)。 * thunk-redis 提供对 Node.js 和 io.js 的支持,它是一个基于 thunk/promise 的 redis 客户端,具有流水线和集群功能。 * redis-go-cluster 是用于 Go 语言的 Redis Cluster 的实现,使用 Redigo 库客户端作为基本客户端。通过结果聚合实现 MGET/MSET。 * ioredis 是一个流行的 Node.js 客户端,为 Redis 群集提供了强大的支持。 * 当使用 '''-c''' 开关启动时,'''redis-cli''' 实用程序实现基本群集支持。 测试 Redis 群集的一个简单方法是尝试上述任何一个客户端,或者简单地使用 redis-cli 命令行实用程序。 : 以下是使用后者进行交互的示例: : <syntaxhighlight lang="bash" highlight=""> $ redis-cli -c -p 7000 redis 127.0.0.1:7000> set foo bar -> Redirected to slot [12182] located at 127.0.0.1:7002 OK redis 127.0.0.1:7002> set hello world -> Redirected to slot [866] located at 127.0.0.1:7000 OK redis 127.0.0.1:7000> get foo -> Redirected to slot [12182] located at 127.0.0.1:7002 "bar" redis 127.0.0.1:7002> get hello -> Redirected to slot [866] located at 127.0.0.1:7000 "world" </syntaxhighlight> redis-cli 只有非常基本的集群支持,因此它始终使用这样一个事实,即 redis 群集节点能够将客户端重定向到正确的节点。 一个严肃的客户端可以做得更好,缓存哈希槽和节点地址之间的映射,直接使用到正确节点的正确连接。仅当群集配置中发生更改时才会刷新映射(例如,在故障切换后或系统管理员通过添加或删除节点更改群集布局后)。 === 重新分片(Resharding) === Resharding 基本上意味着'''将哈希槽从一组节点移动到另一组节点''',与创建集群一样,它是使用 redis-cli 实用程序完成的。 # 要开始重新分片,只需键入: #: <syntaxhighlight lang="bash" highlight=""> redis-cli --cluster reshard 127.0.0.1:7000 </syntaxhighlight> #* 您只需指定集群中的一个节点,redis-cli 将自动查找其他节点。 #* 目前,redis-cli 只能在管理员支持下重新硬存储,您不能只说将 5% 的插槽从该节点移动到另一个节点(但实现起来非常简单)。所以它以问题开始。 # 第一个问题是你想做多少大的重做: #: <syntaxhighlight lang="bash" highlight=""> How many slots do you want to move (from 1 to 16384)? </syntaxhighlight> #: 我们可以尝试重新硬处理1000个散列槽; # 然后 redis-cli 需要知道重新存储的目标是什么,即接收哈希槽的节点。 #: 我将使用第一个主节点,即127.0.0.1:7000(节点ID:97a3a64667477371c4479320d683e4c8db5858b1); #* 需要指定实例的节点 ID。redis-cli已将其打印在列表中,但如果需要,始终可以使用以下命令找到节点的 ID: #*: <syntaxhighlight lang="bash" highlight=""> $ redis-cli -p 7000 cluster nodes | grep myself 97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5460 </syntaxhighlight> # 现在,将询问您想要从哪些节点获取这些密钥。 #* 键入 all 以便从所有其他主节点获取一点哈希槽。 #: 最终确认后,您将看到 redis-cli 将从一个节点移动到另一个节点的每个插槽的消息,并且将为从一侧移动到另一侧的每个实际键打印一个点。 # 在重新分片结束时,可以使用以下命令测试集群的运行状况: #: <syntaxhighlight lang="bash" highlight=""> redis-cli --cluster check 127.0.0.1:7000 </syntaxhighlight> #: 所有的插槽都将像往常一样被覆盖,但这次 127.0.0.1:7000 的主插槽将有更多的哈希插槽,大约 6461 个。 === 为重新分片操作编写脚本 === 可以自动执行 Resharding,而无需以交互方式手动输入参数。 这可以使用以下命令行: <syntaxhighlight lang="bash" highlight=""> redis-cli --cluster reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes </syntaxhighlight> * 如果您可能经常重新硬盘,这允许构建一些自动化,但是目前 redis-cli 无法自动重新平衡群集检查 keys 在群集节点上的分布并根据需要智能移动插槽。此功能将在将来添加。 * “--cluster-yes”选项指示集群管理器对命令的提示自动回答“yes”,从而允许它在非交互模式下运行。请注意,还可以通过设置 REDISCLI_CLUSTER_YES 环境变量来激活此选项。 === 测试故障转移 === 为了触发故障转移,我们可以做的最简单的事情(这也是分布式系统中可能发生的语义上最简单的故障)是使单个进程崩溃,在我们的例子中是使单个主进程崩溃。 # 我们可以通过以下命令识别 maser 并使其崩溃: #: <syntaxhighlight lang="bash" highlight=""> $ redis-cli -p 7000 cluster nodes | grep master 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385482984082 0 connected 5960-10921 2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 master - 0 1385482983582 0 connected 11423-16383 97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422 </syntaxhighlight> #: 如上 7000、7001 和 7002 是 maser。让我们使用 '''DEBUG SEGFAULT''' 命令使节点 7002 崩溃: #: <syntaxhighlight lang="bash" highlight=""> $ redis-cli -p 7002 debug segfault Error: Server closed the connection </syntaxhighlight>
返回至“
Redis:集群教程
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
大陆简体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
笔记
服务器
数据库
后端
前端
工具
《To do list》
日常
阅读
电影
摄影
其他
Software
Windows
WIKIOE
所有分类
所有页面
侧边栏
站点日志
工具
链入页面
相关更改
特殊页面
页面信息