草庐IT

Redis 集群模式

YeeXang 2023-03-28 原文

概述

Redis 在 3.0 之后开始支持 Cluster(集群)模式,特点如下:

  • 支持节点的自动发现:可向集群动态添加节点,并自动融入
  • 支持 slave-master 选举和容错:多个 master 宕机后,选举出新的 master 继续工作
  • 在线分片:当有新的节点加入时,为新节点分配 slot 空间,从其他节点处获取,并携带数据


搭建

以 Ubuntu 18.04.5,redis-7.0.4 为例,要想让集群正常运作至少需要三个 master 节点,为了高可用,每个 master 节点至少要有一个 slave 节点,这里以一台机器的六个端口作演示,分别是 7000、7001、7002、7003、7004、7005

创建多个 redis 配置文件(以其中一个为例,区别在于端口号和部分配置文件命名)

# 开启远程连接
bind * -::*
# 配置端口号,每个配置文件分别配置 7001/.../7005
port 7000
# 启用守护进程
daemonize yes
# 进程配置文件名,redis_7000/.../redis_7005
pidfile "/var/run/redis_7000.pid"
# rdb文件名,dump-7000/.../dump-7005
dbfilename dump-7000.rdb
# 开启 AOF 持久化
appendonly yes  
# aof文件名,appendonly-7000/./appendonly-7005
appendfilename "appendonly-7000.aof"
# 开启集群模式
cluster-enabled yes
# 节点配置文件名,nodes-7000/.../nodes-7005
cluster-config-file nodes-7000.conf
# 节点超时时间
cluster-node-timeout 15000

创建 7000-7005 文件夹,把对应的配置文件放进去

指定不同配置文件启动六个节点,例如

./redis-server 7000/redis.conf

将六个节点组合成集群,在组合之前,请确保以下条件:

  • 所有节点正常启动后
  • nodes-xxx.conf 文件都正常生成
  • 如果有开启防火墙,放行 7000-7005、17000-17005 端口,前者用于客户端通信,后者用于集群间通信
# redis会自动规划集群部署,例如这里会以前三个为主节点,后三个分别作从节点
redis-cli --cluster create --cluster-replicas 1 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

使用 redis-cli --cluster check 命令来查看节点信息

# 后面的ip:port使用集群任一节点即可
redis-cli --cluster check 127.0.0.1:7000

也可以通过 redis-cli 连接任一节点,使用如下命令查看节点信息

# 使用普通方式登录,存储数据时可能会出现MOVED重定向操作,所以应该加上-c以集群方式登录
redis-cli -c -p 7000
# 查看节点所在集群信息
cluster nodes

一般使用 cluster nodes 命令,信息更详细

以 7000 为例,讲解每项的含义:

  • id:28df8f39d35ad2ddaeddad2ef0b11c77f381a26a,节点 ID,这个值在节点启动时创建,作为唯一标识
  • ip:port:127.0.0.1:7000@17000,节点的通信地址
  • flags:myself,master:逗号分割的标记位,可能的值及含义如下:
    • myself:当前连接的节点
    • master:节点是 master
    • slave: 节点是 slave
    • fail?:当前节点无法联系,但逻辑上是可达的(非 FAIL 状态)
    • fail:节点处于 FAIL 状态.,大部分节点都无法与其取得联系,则会将该节点由 PFAIL 状态升级为 FAIL 状态
    • handshake:还未取得信任的节点,当前正在与其进行握手
    • noaddr:没有地址的节点(No address known for this node)
    • noflags:连个标记都没有(No flags at all)
  • master:28df8f39d35ad2ddaeddad2ef0b11c77f381a26a,这里是 7004 节点的信息,因为该节点是 slave 节点,所以会列出所属 master 节点的 ID,也就是 7000
  • ping-sent:-0,最近一次发送 ping 的时间,这个时间是一个 unix 毫秒时间戳,0 代表没有发送过
  • pong-recv:1661445823000,最近一次收到pong的时间,使用 unix 时间戳表示
  • config-epoch:1,节点的 epoch 值(如果该节点是从节点,则为其主节点的 epoch 值),每当节点发生失败切换时,都会创建一个新的且递增的 epoch 值,如果多个节点竞争同一个哈希槽,epoch 值更高的节点更容易抢夺到
  • link-state:connected,集群总线使用的链接的状态,我们使用这个链接与集群中其他节点进行通信,值可以是 connected 或 disconnected
  • slot:0-5460,哈希槽值或者一个哈希槽范围,有关哈希槽值的内容在下面讲解

哈希槽(slot)

Redis 集群把把所有的物理节点(向外提供服务的 master)映射到 [0 - 16383] slot 上,即所有 master 均分 16384 大小的 slot 空间,每个 master 占有一定的 slot 空间

当存储数据时,首先会对 key 使用 CRC16 算法进行加密,将得到的值 mod 16384,这样一来 key 的值始终在 0-16383 之间,Redis 则根据 key 值将其放入对应范围的节点,比如算出来的 key 值是 3000,7000 节点的 slot 空间是 [0 - 5460],那么就会放到 7000 节点,平均集群的压力

同理,当获取数据时,也会对 Key 进行同样的计算,根据得到的值去对应的节点获取数据

注意:slot 不是值集群所能存储数据的容量,16384 也并不是说只能存 16384 个键,它仅仅表示每个节点的一个存储范围,key 值落到那个范围,就找对应范围的节点服务

以之前搭建的集群为例,客户端连接 7000 节点,存储一个测试数据,Redis 会自动计算 Key 值,如果计算结果在 7001 节点的范围,则重定向到 7001 节点存储

获取值也是同理


其他命令

向集群动态添加主节点,先启动 7006 服务,再执行以下命令:

# redis-cli --cluster add-node [新加入节点] [原始集群中任意节点]
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000

向集群动态添加从节点,先启动 7007 服务,再执行以下命令:

# redis-cli --cluster add-node --cluster-slave [--master-id master节点id] [新加入节点] [原始集群中任意节点]
# 如果没有指定主节点,redis会随机给副本数少的主节点添加当前副本节点
redis-cli --cluster add-node --cluster-slave 127.0.0.1:7007 127.0.0.1:7000

这个时候我们查看集群状态,会发现新添加的 7006 节点没有分配 slot

没有 slot 是不能处理数据的,所以需要重新分配集群 slot

# redis-cli --cluster reshard [原始集群中任意节点] --cluster-from [源slot节点id,多个id用逗号分隔] --cluster-to [接收slot节点id] --cluster-slots [分配slot大小]
redis-cli --cluster reshard 127.0.0.1:7000 --cluster-from 28df8f39d35ad2ddaeddad2ef0b11c77f381a26a --cluster-to a8e9320b93498f2dcdc689e61ffec73f79d3a6d6 --cluster-slots 1024

删除节点

# redis-cli --cluster del-node [集群中任意节点] [删除节点id]
redis-cli --cluster del-node 127.0.0.1:7000 ac949f85aa8becb811d1f0a46d18f41075e63051

有关Redis 集群模式的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  4. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  5. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

  6. Ruby:标准递归模式 - 2

    我经常迷上ruby​​的一件事是递归模式。例如,假设我有一个数组,它可能包含无限深度的数组作为元素。所以,例如:my_array=[1,[2,3,[4,5,[6,7]]]]我想创建一个方法,可以将数组展平为[1,2,3,4,5,6,7]。我知道.flatten可以完成这项工作,但这个问题是作为我经常遇到的递归问题的一个例子-因此我试图找到一个更可重用的解决方案。简而言之-我猜这种事情有一个标准模式,但我想不出任何特别优雅的东西。任何想法表示赞赏 最佳答案 递归是一种方法,它不依赖于语言。您在编写算法时要考虑两种情况:再次调用函数的情

  7. ruby - 在 Ruby 中查找多个正则表达式匹配的模式和位置 - 2

    这应该是一个简单的问题,但我找不到任何相关信息。给定一个Ruby中的正则表达式,对于每个匹配项,我需要检索匹配的模式$1、$2,但我还需要匹配位置。我知道=~运算符为我提供了第一个匹配项的位置,而string.scan(/regex/)为我提供了所有匹配模式。如果可能,我需要在同一步骤中获得两个结果。 最佳答案 MatchDatastring.scan(regex)do$1#Patternatfirstposition$2#Patternatsecondposition$~.offset(1)#Startingandendingpo

  8. ruby - sinatra 框架的 MVC 模式 - 2

    我想开始使用“Sinatra”框架进行编码,但我找不到该框架的“MVC”模式。是“MVC-Sinatra”模式或框架吗? 最佳答案 您可能想查看Padrino这是一个围绕Sinatra构建的框架,可为您的项目提供更“类似Rails”的感觉,但没有那么多隐藏的魔法。这是使用Sinatra可以做什么的一个很好的例子。虽然如果您需要开始使用这很好,但我个人建议您将它用作学习工具,以对您来说最有意义的方式使用Sinatra构建您自己的应用程序。写一些测试/期望,写一些代码,通过测试-重复:)至于ORM,你还应该结帐Sequel其中(imho

  9. ruby-on-rails - Rails 如何创建数据模式种子数据 - 2

    有没有一种方法可以自动生成种子数据文件并创建种子数据,就像您在下面链接中的Laravel中看到的那样?LaravelDatabaseMigrations&Seed我在另一个应用程序上看到在Rails的db文件夹下创建了一些带有时间戳的文件,其中包含种子数据。创建它的好方法是什么? 最佳答案 我建议你使用Fabrication的组合gem和Faker.Fabrication允许您编写一个模式来构建您的对象,而Faker为您提供虚假数据,如姓名、电子邮件、电话号码等。这是制造商的样子:Fabricator(:user)dousernam

  10. ruby-on-rails - Ruby on Rails 应用程序的只读模式 - 2

    我有一个交互式RubyonRails应用程序,我想在特定时间将其置于“只读模式”。这将允许用户读取他们需要的数据,但阻止他们执行写入数据库的操作。执行此操作的一种方法是在数据库中放置一个true/false变量,该变量在进行任何写入之前进行检查。我的问题。有没有更优雅的解决方案来解决这个问题? 最佳答案 如果你真的想阻止任何数据库写入,我能想到的最简单的方法是覆盖readonly?始终返回true的模型方法,无论是在选定模型中还是对于所有ActiveRecord模型。如果模型设置为只读(通常通过调用#readonly!来完成),任何

随机推荐