Redis学习之Redis Cluster(三)

Redis Cluster

Redis Cluster

Redis Cluster提供了一种运行Redis安装的方法,其中数据 在多个Redis节点之间自动分片java

Redis Cluster还在分区期间提供必定程度的可用性,其实是在某些节点发生故障或没法通讯时继续运行的能力。可是,若是发生较大的故障(例如,当大多数主设备不可用时),集群将中止运行。node

因此实际上,你对Redis Cluster有什么见解?git

  • 可以在多个节点之间自动拆分数据集
  • 当节点的子集遇到故障或没法与集群的其他部分通讯时,可以继续操做

Redis集群TCP端口

每一个Redis集群节点都须要打开两个TCP链接。用于为客户端提供服务的普通Redis TCP端口,例如6379,加上经过向数据端口添加10000得到的端口,所以示例中为16379。github

第二个端口用于集群总线,即便用二进制协议的节点到节点的通讯通道。节点使用集群总线进行故障检测,配置更新,故障转移受权等。客户端永远不该尝试与集群总线端口通讯,但始终使用正常的Redis命令端口,但请确保在防火墙中打开两个端口,不然Redis集群节点将没法通讯。redis

命令端口和集群总线端口偏移是固定的,始终为10000。docker

请注意,对于每一个节点,要使Redis集群正常工做,您须要:数据库

  1. 用于与客户端通讯的普通客户端通讯端口(一般为6379),对全部须要访问集群的客户端以及全部其余集群节点(使用客户端端口进行key迁移)开放。
  2. 必须能够从全部其余集群节点访问集群总线端口(客户端端口+ 10000)。

若是不打开两个TCP端口,则集群将没法按预期工做。promise

集群总线使用不一样的二进制协议进行节点到节点的数据交换,这更适合于使用不多的带宽和处理时间在节点之间交换信息。缓存

Redis集群和Docker

目前,Redis集群不支持NATted环境,也不支持从新映射IP地址或TCP端口的通常环境。安全

Docker使用一种称为端口映射的技术:与程序使用的端口相比,在Docker容器内运行的程序可能会使用不一样的端口。这对于在同一服务器中同时使用相同端口运行多个容器很是有用。

为了使Docker与Redis Cluster兼容,您须要使用Docker 的主机网络模式。有关更多信息,请查看Docker文档中--net=host选项。

Redis集群数据分片

Redis Cluster不使用一致的散列,而是使用不一样形式的分片,其中每一个键在概念上都是咱们称之为散列槽的一部分

Redis集群中有16384个散列槽,为了计算给定key的散列槽,咱们只需采用key模数为16384的CRC16。

Redis集群中的每一个节点都负责哈希槽的子集,例如,您可能拥有一个包含3个节点的集群,其中:

  • 节点A包含从0到5500的散列槽。
  • 节点B包含从5501到11000的散列槽。
  • 节点C包含从11001到16383的散列槽。

这容许轻松添加和删除集群中的节点。例如,若是我想添加一个新节点D,我须要将一些哈希槽从节点A,B,C移动到D。一样,若是我想从集群中删除节点A,我只需移动A服务的哈希槽到B和C。当节点A为空时,我能够彻底从集群中删除它。

由于将哈希槽从一个节点移动到另外一个节点不须要中止操做,添加和删除节点,或者更改节点所持有的哈希槽的百分比,因此不须要任何停机时间。

只要涉及单个命令执行(或整个事务或Lua脚本执行)的全部键都属于同一个哈希槽,Redis Cluster就支持多个键操做。用户能够经过使用称为哈希标记的概念强制多个key成为同一哈希槽的一部分。

Hash标签记录在Redis集群规范中,但要点是若是key中{}括号之间有子字符串,则只对字符串内部的内容进行哈希处理,例如,保证this{foo}keyanother{foo}key 位于相同的哈希槽中,可使多个键在一个参数的命令中一块儿使用。

Redis Cluster主从模型

为了在主节点子集发生故障或没法与大多数节点通讯时保持可用,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 Cluster将没法继续运行。

Redis集群一致性保证

Redis Cluster没法保证强一致性。实际上,这意味着在某些条件下,Redis Cluster可能会丢失系统向客户端确认的写入。

Redis Cluster可能丢失写入的第一个缘由是它使用异步复制。这意味着在写入期间会发生如下状况:

  • 您的客户端写入主B。
  • masterB向您的客户回复肯定。
  • 主设备B将写入传播到其从设备B1,B2和B3。

正如你所看到的,B在回复客户端以前并无等待来自B1,B2,B3的确认,由于这对Redis来讲是一个太高的延迟惩罚,因此若是你的客户端写了一些内容,B会确认写入,可是崩溃在写入发送到其从属以前,其中一个从属(没有接收到写入)能够提高为master,将会永远丢失写入。

这与配置为每秒将数据刷新到磁盘的大多数数据库所发生的状况很是类似,所以,因为过去使用不涉及分布式系统的传统数据库系统的经验,所以您已经可以推断这种状况。一样,您能够经过在回复客户端以前强制数据库刷新磁盘上的数据来提升一致性,但这一般会致使性能太低。在Redis Cluster的状况下,这至关于同步复制。

基本上须要在性能和一致性之间进行权衡。

Redis Cluster在绝对须要时支持同步写入,经过WAIT命令实现,这使得丢失写入的可能性大大下降,但请注意,即便使用同步复制,Redis Cluster也不会实现强一致性:在更复杂的状况下老是能够实现失败场景,没法接收写入的slave被选为master。

还有另外一个值得注意的状况是,Redis集群将丢失写入,这种状况发生在网络分区中,其中客户端与少数实例(至少包括主服务器)隔离。

以6个节点簇为例,包括A,B,C,A1,B1,C1,3个master和3个slave。还有一个客户,咱们称之为Z1。

在发生分区以后,可能在分区的一侧有A,C,A1,B1,C1,在另外一侧有B和Z1。

Z1仍然能够写入B,它将接受其写入。若是分区在很短的时间内恢复,集群将继续正常运行。可是,若是分区持续足够的时间使B1在分区的多数侧被提高为主,则Z1发送给B的写入将丢失。

请注意,Z1将可以发送到B的写入量存在最大窗口:若是分区的多数方面已经有足够的时间将从属设备选为主设备,则少数端的每一个主节点都会中止接受写入。

这段时间是Redis Cluster的一个很是重要的配置指令,称为节点超时

节点超时事后,主节点被视为失败,能够由其中一个副本替换。相似地,在节点超时已通过去而主节点没法感知大多数其余主节点以后,它进入错误状态并中止接受写入。

Redis集群配置参数

咱们即将建立一个示例集群部署。在继续以前,让咱们介绍Redis Cluster在redis.conf文件中引入的配置参数。有些会很明显,有些会在你继续阅读时更清楚。

  • cluster-enabled<yes/no>:若是是,则在特定Redis实例中启用Redis集群支持。不然,实例像往常同样做为独立实例启动。
  • cluster-config-file:请注意,尽管有此选项的名称,但这不是用户可编辑的配置文件,而是每次发生更改时Redis集群节点自动保持集群配置(基本上是状态)的文件,为了可以在启动时从新阅读它。该文件列出了集群中其余节点,状态,持久变量等内容。因为某些消息接收,一般会将此文件重写并刷新到磁盘上。
  • cluster-node-timeout:Redis集群节点不可用的最长时间,将会被视为失败。若是主节点的可访问时间超过指定的时间,则其从属节点将进行故障转移。此参数控制Redis集群中的其余重要事项。值得注意的是,在指定时间内没法访问大多数主节点的每一个节点都将中止并接受查询。
  • cluster-slave-validity-factor:若是设置为零,则无论master和slave之间链路保持断开链接的时间长短,slave将始终尝试对master进行故障切换。若是该值为正,则计算timeout值乘以此选项提供的因子做为最大断开时间,若是节点是从属节点,则若是主连接断开链接的时间超过指定的时间,则不会尝试启动故障转移。例如,若是节点超时设置为5秒,而且有效性因子设置为10,则从主设备断开超过50秒的从设备将不会尝试故障转移其主设备。请注意,若是没有slave可以对其进行故障转移,则任何不一样于零的值均可能致使Redis集群在master发生故障后不可用。在这种状况下,只有当原始主服务器从新加入集群时,集群才会返回可用状态。
  • cluster-migration-barrier:主服务器将保持链接的最小从服务器数,以便另外一个从服务器迁移到没有任何从服务器覆盖的主服务器。有关详细信息,请参阅本教程中有关副本迁移的相应部分。
  • cluster-require-full-coverage<yes/no>:若是设置为yes,则默认状况下,若是任何节点未覆盖某个百分比的slots数量,则集群将中止接受写入。若是该选项设置为no,即便只能处理有关键子集的请求,集群仍将提供查询。

建立和使用Redis集群

注意:要手动部署Redis集群**,了解它的某些操做方面很是重要**。可是,若是要启动集群并尽快运行,请跳过本节和下一节,而后直接使用create-cluster脚本建立Redis集群

要建立集群,咱们首先要作的是在集群模式下运行一些空的Redis实例。这基本上意味着不使用普通Redis实例建立集群,由于须要配置特殊模式,以便Redis实例启用集群特定功能和命令。

如下是最小的Redis集群配置文件:

port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
复制代码

正如您所看到的,启用集群模式的只是cluster-enabled 指令。每一个实例还包含存储此节点配置的文件路径,默认状况下为nodes.conf。这个文件永远不会被人类接触;它只是在Redis Cluster实例启动时生成,并在每次须要时更新。copy的时候,记得修改nodes.conf的名字,如nodes-7001.conf

请注意,按预期工做的最小集群须要包含至少三个主节点。对于您的第一次测试,强烈建议启动具备三个主设备和三个从设备的六节点集群。

为此,请输入一个新目录,并建立以咱们将在任何给定目录中运行的实例的端口号命名的如下目录。

就像是:

mkdir cluster-test
cd cluster-test
mkdir 7000 7001 7002 7003 7004 7005
复制代码

redis.conf在每一个目录中建立一个文件,从7000到7005做为配置文件的模板,只需使用上面的小例子,但请确保根据目录名称用正确的端口号替换端口号7000

如今将从GitHub的unstable分支中的最新源编译的 redis-server可执行文件复制到cluster-test目录中,最后在您喜欢的终端应用程序中打开6个终端tabs。

像这样开始每一个实例,每一个tab一个:

cd 7000
../redis-server ./redis.conf
复制代码

从每一个实例的日志中能够看出,因为不存在任何nodes.conf文件,所以每一个节点都会为本身分配一个新ID。

[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1 复制代码

此特定实例将永久使用此ID,以使实例在集群的上下文中具备惟一名称。每一个节点都使用此ID记住每一个其余节点,而不是经过IP或端口记住。IP地址和端口可能会发生变化,但惟一的节点标识符永远不会在节点的整个生命周期内发生变化。咱们称这个标识符为Node ID

建立集群

如今咱们已经运行了许多实例,咱们须要经过向节点编写一些有意义的配置来建立咱们的集群。

若是您使用的是Redis 5,这很容易实现,由于咱们在嵌入的Redis Cluster命令行实用程序redis-cli的帮助下,可用于建立新集群,检查或从新塑形现有集群等等。

对于Redis版本3或4,有一个名为redis-trib.rb很是类似的旧工具。您能够src在Redis源代码分发的目录中找到它。你须要安装redisgem才能运行redis-trib

gem install redis
复制代码

第一个例子,即集群建立,将在Redis 5的redis-cli和Redis 3和4中的redis-trib同时显示。可是全部下面的例子都只会使用redis-cli,由于你能够看到语法很是类似,你能够经过使用redis-trib.rb help获取有关旧语法的信息将一个命令行更改成另外一个命令行。**重要提示:**请注意,若是您愿意,可使用Redis 5 的redis-cli代替Redis 4集群。

要为Redis 5建立集群,redis-cli只需键入(若是redis有密码记得加入-a参数,要远程调用请配置公网ip):

redis-cli -a 123456 --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
复制代码

使用redis-trib.rb用于Redis的4或3:

./redis-trib.rb create --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
复制代码

这里使用的命令是create,由于咱们想要建立一个新的集群。该选项--cluster-replicas 1意味着咱们但愿每一个建立的主服务器都有一个从服 其余参数是我要用于建立新集群的实例的地址列表。

显然,咱们要求的惟一设置是建立一个包含3个主服务器和3个从服务器的集群。

Redis-cli将为您提供配置。键入yes接受建议的配置。将配置并加入集群,这意味着实例将被引导为彼此通讯。最后,若是一切顺利,你会看到这样的消息:

[OK] All 16384 slots covered

这意味着至少有一个主实例为16384个可用插槽提供服务。

使用create-cluster脚本建立Redis集群

若是您不想经过如上所述手动配置和执行单个实例来建立Redis集群,则可使用更简单的系统(但您不会学习相同数量的操做详细信息)。

只需检查Redis发行版中的utils/create-cluster目录便可。create-cluster内部有一个脚本(与其包含的目录同名),它是一个简单的bash脚本。要启动具备3个主服务器和3个从服务器的6节点集群,只需键入如下命令:

一、create-cluster start

二、create-cluster create

yesredis-cli实用程序但愿您接受集群布局时,在步骤2中回复。

您如今能够与集群交互,默认状况下,第一个节点将从端口30001开始。完成后,使用如下命令中止集群:

一、create-cluster stop

README有关如何运行脚本的更多信息,请阅读此目录中的内容。

玩集群

在此阶段,Redis Cluster的一个问题是缺乏客户端库实现。

我知道如下实现:

  • redis-rb-cluster是由我(@antirez)编写的做为其余语言的参考的Ruby实现。它是原始redis-rb的简单包装器,实现了最小的语义,能够有效地与集群通讯。
  • redis-py-cluster redis-rb-cluster到Python的一个端口。支持大多数redis-py功能。正在积极发展。
  • 流行的Predis支持Redis Cluster,最近更新了支持而且正在积极开发中。
  • 最经常使用的Java客户端Jedis最近添加了对Redis Cluster的支持,请参阅项目README中的Jedis Cluster部分。
  • StackExchange.Redis提供对C#的支持(而且应该适用于大多数.NET语言; VB,F#等)
  • thunk-redis支持Node.js和io.js,它是一个基于thunk / promise的redis客户端,具备流水线和集群。
  • redis-go-cluster是使用Redigo库客户端做为基本客户端的Go语言的Redis集群的实现。经过结果聚合实现MGET / MSET。
  • 在GitHub上的Redis的存储库中的不稳定分支中的redis-cli工具实现了一个很是基本的集群支持-c开关。

测试Redis Cluster的一种简单方法是尝试上述任何客户端或仅使用redis-cli命令行实用程序。如下是使用后者的交互示例:

$ 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:7000> get hello
-> Redirected to slot [866] located at 127.0.0.1:7000
"world"
复制代码

**注意:**若是使用脚本建立集群,则节点能够侦听不一样的端口,默认状况下从30001开始。

redis-cli集群支持很是基础,所以它老是使用Redis集群节点可以将客户端重定向到右节点的事实。一个认真的客户端可以作得更好,并在哈希槽和节点地址之间缓存地图,以直接使用与正确节点的正确链接。仅当集群配置中的某些内容发生更改时(例如,在故障转移以后或系统管理员经过添加或删除节点更改集群布局后),才会刷新映射。

使用redis-rb-cluster编写示例应用程序

在继续展现如何操做Redis集群,执行故障转移或从新分片以前,咱们须要建立一些示例应用程序,或者至少可以理解简单的Redis集群客户端交互的语义。

经过这种方式,咱们能够运行一个示例,同时尝试使节点失败,或者开始从新分片,以了解Redis Cluster在真实条件下的行为方式。观察集群时没有数据写入并非颇有帮助。

本节介绍了redis-rb-cluster的一些基本用法, 展现了两个示例。第一个是如下内容,是redis-rb-cluster发行版中的文件 example.rb

1  require './cluster'
   2
   3  if ARGV.length != 2
   4      startup_nodes = [
   5          {:host => "127.0.0.1", :port => 7000},
   6          {:host => "127.0.0.1", :port => 7001}
   7      ]
   8  else
   9      startup_nodes = [
  10          {:host => ARGV[0], :port => ARGV[1].to_i}
  11      ]
  12  end
  13
  14  rc = RedisCluster.new(startup_nodes,32,:timeout => 0.1)
  15
  16  last = false
  17
  18  while not last
  19      begin
  20          last = rc.get("__last__")
  21          last = 0 if !last
  22      rescue => e
  23          puts "error #{e.to_s}"
  24          sleep 1
  25      end
  26  end
  27
  28  ((last.to_i+1)..1000000000).each{|x|
  29      begin
  30          rc.set("foo#{x}",x)
  31          puts rc.get("foo#{x}")
  32          rc.set("__last__",x)
  33      rescue => e
  34          puts "error #{e.to_s}"
  35      end
  36      sleep 0.1
  37  }
复制代码

应用程序作了一件很是简单的事情,它将表单中的键foo<number>一个接一个设置为number。所以,若是您运行该程序,结果是如下命令流:

  • SET foo0 0
  • SET foo1 1
  • SET foo2 2
  • 等等...

该程序看起来比它应该更复杂,由于它被设计为在屏幕上显示错误而不是以异常退出,所以使用集群执行的每一个操做都由begin rescue块包装。

14行是该程序中第一个有趣的行。它建立Redis Cluster对象,使用startup nodes list做为参数,容许此对象对不一样节点采用不一样的最大链接数,最后在给定timeout,被认为给定的失败操做。

不须要集群的全部节点都启动。重要的是至少有一个节点是可达的。另请注意,只要redis-rb-cluster可以与第一个节点链接,它就会更新此启动节点列表。您应该指望与任何其余严肃的客户端都有这样的行为。

既然咱们已将Redis Cluster对象实例存储在rc变量中,咱们就可使用该对象,就好像它是一个普通的Redis对象实例同样。

这正是第18到26行所发生的事情:当咱们从新启动示例时,咱们不想再次启动foo0,所以咱们将计数器存储在Redis自己中。上面的代码用于读取此计数器,或者若是计数器不存在,则将其赋值为零。

但请注意它是如何循环,由于咱们想要反复尝试,即便集群已关闭并返回错误。普通应用程序不须要那么当心。

28到37之间的行启动主循环,其中设置了键或显示错误。

注意sleep循环结束时的调用。在你的测试中,若是你想尽量快地写入集群,你能够删除sleep(相对于这是一个繁忙的循环,固然没有真正的并行性,因此在最好的条件下,你一般会获得10k的操做/秒)。

一般,写入速度会下降,以便示例应用程序更容易被人类遵循。

启动应用程序会生成如下输出:

ruby ./example.rb
1
2
3
4
5
6
7
8
9
^C (I stopped the program here)
复制代码

这不是一个很是有趣的程序,咱们稍后会使用更好的程序,但咱们已经能够看到程序运行时从新分片期间会发生什么。

从新整理集群

如今咱们准备尝试集群从新分片了。为此,请保持example.rb程序运行,以便您能够查看是否对运行的程序有一些影响。此外,您可能想要继续执行该sleep 调用,以防止在从新分片期间有一些更严重的写入负载。

从新分片基本上意味着将散列槽从一组节点移动到另外一组节点,而且像集群建立同样,它是使用redis-cli实用程序完成的。

要开始从新分片,只需输入:

redis-cli --cluster reshard 127.0.0.1:7000

您只需指定一个节点,redis-cli将自动找到其余节点。

目前redis-cli只能经过管理员支持从新加载,你不能只说将5%的插槽从这个节点移动到另外一个节点(但这实现起来很是简单)。因此它从问题开始。首先是你要作多少大的从新分数:

How many slots do you want to move (from 1 to 16384)?

咱们能够尝试从新设置1000个散列槽,若是示例在没有调用sleep的状况下运行,那么该散列槽应该包含很是少许的key。

而后redis-cli须要知道从新分片的目标是什么,即接收哈希槽的节点。我将使用第一个主节点,即127.0.0.1:7000,但我须要指定实例的节点ID。这已由redis-cli打印在列表中,但若是须要,我总能使用如下命令找到节点的ID:

$ redis-cli -p 7000 cluster nodes | grep myself
97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5460
复制代码

好的,个人目标节点是97a3a64667477371c4479320d683e4c8db5858b1。

如今,您将被问到要从哪些节点获取这些key。我只是输入all以便从全部其余主节点获取一些哈希槽。

在最终确认以后,您将看到redis-cli将从一个节点移动到另外一个节点的每一个插槽的消息,而且将为从一侧移动到另外一侧的每一个实际键打印一个点。

从新分片正在进行中时,您应该可以看到您的示例程序不受影响地运行。若是须要,您能够在从新分片期间屡次中止并从新启动它。

在从新分片结束时,您可使用如下命令测试集群的运行情况:

redis-cli --cluster check 127.0.0.1:7000

全部的插槽都会像往常同样被覆盖,但此次127.0.0.1:7000的主机将有更多的散列槽,大约6461。

编写从新分析操做的脚本

能够自动执行从新分片,而无需以交互方式手动输入参数。这可使用以下命令行:

redis-cli reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes
复制代码

若是您可能常常从新设置,则容许构建一些自动操做,可是目前没法redis-cli自动从新平衡集群,检查跨集群节点的key分发以及根据须要智能地移动插槽。此功能将在将来添加。

手动故障转移

有时强制进行故障转移而不会在主服务器上形成任何问题。例如,为了升级其中一个主节点的Redis进程,最好对其进行故障转移,以便将其转换为从属,对可用性的影响最小。

Redis Cluster使用CLUSTER FAILOVER 命令支持手动故障转移,该命令必须在要故障转移的master的一个slave中执行。

手动故障转移是特殊的,与实际主故障致使的故障转移相比更安全,由于它们以免数据丢失的方式发生,经过仅在系统肯定新的主服务器时将客户端从原始主服务器切换到新主服务器master处理了旧的复制流。

这是您在执行手动故障转移时在从属日志中看到的内容:

# Manual failover user request accepted.
# Received replication offset for paused master manual failover: 347540
# All master replication stream processed, manual failover can start.
# Start of election delayed for 0 milliseconds (rank #0, offset 347540).
# Starting a failover election for epoch 7545.
# Failover election won: I'm the new master.
复制代码

基本上链接到咱们正在故障的主服务器的客户端被中止。同时主设备将其复制偏移量发送给从设备,该设备等待到达其侧面的偏移量。达到复制偏移量时,将启动故障转移,并通知旧主服务器有关配置开关的信息。在旧主服务器上取消阻止客户端时,会将它们重定向到新主服务器。

添加新节点

添加新节点基本上是添加空节点而后将一些数据移入其中(若是它是新主节点)或者告诉它设置为已知节点的副本(若是它是从属节点)的过程。

咱们将展现二者,从添加新的主实例开始。

在这两种状况下,执行的第一步是添加空节点

这很简单,只须要在端口7006中启动一个新节点(咱们已经在7000到7005之间使用现有的6个节点),其余节点使用相同的配置,端口号除外,因此你应该按顺序作什么符合咱们用于之前节点的设置:

  • 在终端应用程序中建立一个新选项卡。
  • 输入cluster-test目录。
  • 建立一个名为的目录7006
  • 在里面建立一个redis.conf文件,相似于用于其余节点但使用7006做为端口号的文件。
  • 最后启动服务器 ../redis-server ./redis.conf

此时服务器应该正在运行。

如今咱们能够像往常同样使用redis-cli,以便将节点添加到现有集群中。

redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
复制代码

如您所见,我使用add-node命令将新节点的地址指定为第一个参数,并将集群中随机存在节点的地址指定为第二个参数。

实际上,redis-cli在这方面作的不多帮助咱们,它只是向节点发送了一个CLUSTER MEET消息,这也能够手动完成。可是redis-cli在运行以前也会检查集群的状态,因此即便你知道内部是如何工做的,经过redis-cli执行集群操做是个好主意。

如今咱们能够链接到新节点以查看它是否真正加入了集群:

redis 127.0.0.1:7006> cluster nodes
3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385543178575 0 connected 5960-10921
3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385543179583 0 connected
f093c80dde814da99c5cf72a7dd01590792b783b :0 myself,master - 0 0 0 connected
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543178072 3 connected
a211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385543178575 0 connected
97a3a64667477371c4479320d683e4c8db5858b1 127.0.0.1:7000 master - 0 1385543179080 0 connected 0-5959 10922-11422
3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385543177568 3 connected 11423-16383
复制代码

请注意,因为此节点已链接到集群,所以它已可以正确地重定向客户端查询,而且一般是集群的一部分。然而,与其余master相比,它有两个特色:

  • 它没有数据,由于它没有分配的哈希槽。
  • 由于它是没有分配插槽的主设备,因此当从设备想要成为主设备时,它不参与选举过程。

如今可使用resharding功能为此节点分配哈希槽redis-cli。就像咱们在上一节中所作的那样,它只是将一个空节点从新分区的过程。

将新节点添加为副本

添加新副本能够经过两种方式执行。显而易见的是再次使用redis-cli,使用--cluster-slave选项,以下所示:

redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave
复制代码

请注意,此处的命令行与咱们用于添加新主服务器的命令行彻底相同,所以咱们不指定要添加副本的主服务器。在这种状况下,会发生的事情是redis-cli会将新节点做为随机主副本的副本添加到副本较少的主服务器中。

可是,您可使用如下命令行准确指定要使用新副本定位的主控制器:

redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave --cluster-master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
复制代码

这样咱们就能够将新副本分配给特定的主副本。

将副本添加到特定主节点的更手动方法是将新节点添加为空主节点,而后使用CLUSTER REPLICATE命令将其转换为副本 。若是将节点添加为从属节点但您想将其做为不一样主节点的副本移动,则此方法也有效。

例如,为了添加当前服务于11423-16383范围内的哈希槽的节点127.0.0.1:7005的副本,其具备节点ID3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e,我须要作的就是链接新节点(已经添加为空主)并发送命令:

redis 127.0.0.1:7006> cluster replicate 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
复制代码

而已。如今咱们为这组哈希槽提供了一个新的副本,而且集群中的全部其余节点都已知道(须要几秒钟后才能更新它们的配置)。咱们可使用如下命令进行验证:

$ redis-cli -p 7000 cluster nodes | grep slave | grep 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
f093c80dde814da99c5cf72a7dd01590792b783b 127.0.0.1:7006 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617702 3 connected
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617198 3 connected
复制代码

节点3c3a0c ...如今具备两个从设备,在端口7002(现有的一个)和7006(新的一个)上运行。

删除节点

要删除从节点,只需使用redis-cli命令del-node

redis-cli --cluster del-node 127.0.0.1:7000 `<node-id>`
复制代码

第一个参数只是集群中的随机节点,第二个参数是要删除的节点的ID。

您也能够以相同的方式删除主节点,可是为了删除主节点,它必须为空。若是主服务器不为空,则须要将数据从其从新分配给全部其余主节点。

删除主节点的另外一种方法是在其中一个从节点上执行手动故障转移,并在节点变为新主节点的从节点后将其删除。显然,当你想减小集群中实际的主数量时,这没有用,在这种状况下,须要从新分片。

副本迁移

在Redis集群中,只需使用如下命令,就能够随时使用不一样的主服务器从新配置从属服务器进行复制:

CLUSTER REPLICATE <master-node-id>
复制代码

可是,有一种特殊状况,您但愿副本在没有系统管理员帮助的状况下自动从一个主服务器移动到另外一个主服务器。副本的自动从新配置称为副本迁移,而且可以提升Redis集群的可靠性。

注意:您能够在Redis集群规范中阅读副本迁移的详细信息,这里咱们仅提供有关通常概念的一些信息以及您应该从中获益的信息。

您可能但愿让集群副本在特定条件下从一个主服务器移动到另外一个主服务器的缘由是,一般Redis集群与附加到给定主服务器的副本数量同样能够抵御故障。

例如,若是主服务器及其副本同时发生故障,则每一个主服务器具备单个副本的集群没法继续操做,缘由很简单,由于没有其余实例能够拥有主服务器所服务的散列插槽的副本。然而,虽然netsplits可能同时隔离多个节点,但许多其余类型的故障(如单个节点本地的硬件或软件故障)是一类很是值得注意的故障,不太可能同时发生,因此有可能在你的集群中每一个master都有一个slave,slave在凌晨4点被杀死,master在早上6点被杀死。这仍然会致使集群没法再运行。

为了提升系统的可靠性,咱们能够选择为每一个master添加额外的副本,但这很昂贵。副本迁移容许向少数主服务器添加更多从服务器。因此你有10个master,每一个master有1个slave,总共20个实例。可是,例如,您添加了3个实例做为某些主服务器的从属服务器,所以某些主服务器将拥有多个服务器。

对于副本迁移,发生的状况是,若是master没有slave,则来自具备多个slave的master的副本将迁移到孤立master。因此在咱们上面的例子中你的slave凌晨4点关闭以后,另外一个slave将占据它的位置,当master在凌晨5点失败时,仍然有一个slave能够被选举,以便集群能够继续操做。

那么您应该简要了解副本迁移的内容?

  • 集群将尝试从给定时刻具备最大副本数的主服务器迁移副本。
  • 要从副本迁移中受益,您只须要向集群中的单个主服务器添加一些副本,这与主服务器无关。
  • 有一个配置参数能够控制所调用的副本迁移功能cluster-migration-barrier:您能够在redis.confRedis Cluster提供的示例文件中阅读有关它的更多信息。

Java客户端

package com.lamarsan.cluster;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;

import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * className: Test
 * description: TODO
 *
 * @author hasee
 * @version 1.0
 * @date 2019/9/16 20:31
 */
public class Test {
    public static void main(String[] args) {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        // 最大链接数
        poolConfig.setMaxTotal(1);
        // 最大空闲数
        poolConfig.setMaxIdle(1);
        // 最大容许等待时间,若是超过这个时间还未获取到链接,则会报JedisException异常:
        // Could not get a resource from the pool
        poolConfig.setMaxWaitMillis(1000);
        Set<HostAndPort> nodes = new LinkedHashSet<HostAndPort>();
        nodes.add(new HostAndPort("******", 7000));
        nodes.add(new HostAndPort("******", 7001));
        nodes.add(new HostAndPort("******", 7002));
        nodes.add(new HostAndPort("******", 7003));
        nodes.add(new HostAndPort("******", 7004));
        nodes.add(new HostAndPort("******", 7005));
        String password = "******";
        JedisCluster cluster = new JedisCluster(nodes, 10000, 1000, 1000, password, poolConfig);
        cluster.setnx("foo","bar");
        System.out.println(cluster.get("foo"));
        try {
            cluster.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

复制代码
相关文章
相关标签/搜索