[个人翻译]Redis 集群教程(中)

时间:2022-04-25
本文章向大家介绍[个人翻译]Redis 集群教程(中),主要内容包括使用create-cluster角本创建Redis集群、和集群玩耍、集群的重新分片(Resharding)、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

官方原文地址:https://redis.io/topics/cluster-tutorial

水平有限,如果您在阅读过程中发现有翻译的不合理的地方,请留言,我会尽快修改,谢谢。

使用create-cluster角本创建Redis集群

     如果你不想通过配置来创建Redis集群并且不想向上边阐明的一样手动的去执行单独的实例,这里还有更简单的系统(但是你将不会学到等量的操作细节)

     只需查看在Redis发行版本中的utils/create-cluster目录。在里面有一个叫 create-cluster的角本(和包含他的目录名称一样),为了启动包含3个主节点和3个从节点的6节点集群,只需输入下面的命令:

     1.create-cluster start

     2.create-cluster create

     在第2步当redis-trib实用工具程序想让你接受集群的布局时答复为yes.

     现在你可以和你的集群进行交互了,第一个节点将默认使用30001端口启动。当你操作完后,使用下面的命令停止集群:

     1.create-cluster stop

     请阅读此目录内的README文件,以获得有关如何运行角本的更多信息。

和集群玩耍

     在这个阶段Redis集群有一个问题是缺少客户端库实现。

     下面是我知道的实现:

     > redis-rb-cluster是我(@antirez)用Ruby实现的,做为其它语言的参考。这是围绕原始的redis-rb的简单包装,高效实现了和集群通信的最小语义。

     > redis-py-clusterredis-rb-cluster的Python移植版本,支持redis-py的绝大部分功能。还处于积极开发中。

     > 流行的  Predis现在也支持Redis集群,该支持最近刚更新过,并且还在积极开发中。

     > 使用最多的java客户端, Jedis最近新增了对Redis集群的支持,在项目README里查看 Jedis Cluster 章节。

     > StackExchange.Redis 提供对C#的支持(并且适用于大多数.NET语言; VB, F#等)

     > redis-go-cluster 提供对Node.js和io.js的支持,它是基于thunk/promise的redis客户端并含有管道和集群。

     > redis-go-cluster是使用 Redigo library client做为基本客户端的Go语言的Redis集群实现,通过结果聚合实现MGET/MSET。

     > 在GitHub上Redis unstable分支内的redis-cli实用工具在使用-c开关启动时实现了最基本的集群支持。

     测试Redis集群 最简单的方式是尝试使用上述任意一种客户端,或者只是redis-cli命令行工具。

     下面是使用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集群节点将客户端重定向一个到正确节点。一个完善的客户端可以做的更好,并且缓存hash槽和节点地址的映射,用于直接使用正确的连接到正确的节点。这个映射只有在集群配置有变动时才会被刷新,例如故障转移之后或者系统管理员通过地址修改集群层或移除节点之后。

     编写一个redis-rb-cluster实例程序

     在展示如何操作redis集群,像这些执行故障转移,或者重新分片(resharding)之前, 我们需要创建一些示例程序,至少能明白简单的Redis集群客户端交互语义。

     这样我们可以运行一个示例同时尝试让节点发生故障,或者开始重新分片(resharding),以了解在真实环境中Redis集群的行为。这对于了解当集群没有写入命令时发生了什么不是非常有帮助。

     本节使用两个示例来解释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
  •      And so forth...

     这个程序看起来比通常应用更复杂,因为他被设计为在屏幕上显示错误信息,而不是发生异常退出,所以每一个与集群执行的操作都被包在begin recue块中。

     在程序中第14行代码是第一处有意思的地方,他创建了Redis集群对象,使用启动节点(startup nodes )列表作为参数,允许连接这个对象对应不同节点的最大数量,最后在给定的操作超后被认为故障 。

     启动节点不需要集群内的所有节点。重要的是至少有一个节点是可访问的。注意只要redis-rb-cluster能连接到一个节点就会更新启动列表。你应该期待其他严格的客户端也有这样的行为。

     现在我们已经有Redis集群对象的实例保存在rc变量中,我们准备好象使用一般的redis对象实例一样使用这个对象。

     这是确实发生在 18 到 26行:当我们重启个这示例,面不想重新从foo0开始,所以我们把这个计数保存在Redis内。上面的代码被设计为读取这个计数器,或者如果这个计数器不存在,就设置为0.

     然而注意他是一个怎样的while循环,由于我们想一次又一次的尝试,即使集群已关闭并且返回错误。不般的应用程序不需要这么小心。

28到37行开始主要的的循环,循环里设置键值或者显示错误信息。

     注意在循环的最后调用了sleep。在你的测试中如果你想写入到集群的速度足够快那就把sleep去掉(相对于快这里只是一个频繁的循环,当然不是真正的并发,这样在最好的状态下,你通常会得到10k ops/s)。

     一般情况下对于示例程序能容易的被人类跟随会把写入速度放慢。

     开始这个应用程序后就产生下面的输出:

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

     这不是一个非常有趣的程序,我们一会儿会使用一个更好的,但是我们已经可以看到在程序运行起来后在分片期间发生了什么。

集群的重新分片(Resharding)

     现在我们准备尝试集群的重新分片。为此需要example.rb程序保持运行,因此可以看到是否对程序运行过程中有影响。同样的在分片期间为了更严格的写入负载你可能可能想注释掉sleep调用。

     重新分片基本上意味着把哈希槽 从一组节点移动到另一组节点,就像使用redis-trib实用工具完成集群的创建一样。

     只需输入下面的代码就可以开始重新分片: 

./redis-trib.rb reshard 127.0.0.1:7000

     你唯一需要做的就是指定一个节点,redis-trib会自动的找到其他的节点。

     一般情况下需要管理员的支持reids-trib才能重新分片,你不能说只从这个节点移动槽的5%到其它节点(但是这是很空间实现的)。所以从询问开始。第一是你想要做多大的分片:

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

     我们可以尝试对1000个哈希槽分片,如果示例依然在不调用sleep的情况下运行,那么它已经包含了一些重要的键。

     这时,redis-trib需要知道分片的目标什么是 。也就是说将要接收哈希槽的节点。我将使用第一个主节点,也就是127.0.0.1:7000,但是我需要指定实例的节点ID。通过redis-trib已经在一个列表内打印出来了,但是如果需要我经常使用下面的命令来找节点的ID:

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

     ok所以我们的目标节点是 97a3a64667477371c4479320d683e4c8db5858b1.

     现在你会得到一个询问你想从哪些节点来取这些键,为了能从其它所有的主节点取出一些哈希槽我只需输入all.

     在最后的配置后,你会看到每一个redis-trib要从一个节点移动到另一个节点的槽的信息,并且会为每一个从一个节点移动到另一个节点的实际的键打印一个点。

     在分片过程中你会看到对你运行中的示例是没有影响的。在分片期间,只要你想,你可以多次停止或重启你的示例程序 。

     分片结束后,你可以使用下面的命令来测试集群的健康:

     通常将覆盖所有的槽,但这些主节点127.0.0.1:7000 将有更多的哈希槽,大约为6461.