Redis集群知识解析

redis集群在启动的时候就自动在多个节点间分好片。同时提供了分片之间的可用性:当一部分redis节点故障或网络中断,集群也能继续工作。但是,当大面积的节点故障或网络中断(比如大部分的主节点都不可用了),集群就不能使用。

所以,从实用性的角度,Redis集群提供以下功能:

  • 自动把数据切分到多个redis节点中
  • 当一部分节点挂了或不可达,集群依然能继续工作

Redis集群的TCP端口

redis集群中的每个节点都需要建立2个tcp连接,监听这2个端口:一个端口称之为“客户端端口”,用于接受客户端指令,与客户端交互,比如6379;另一个端口称之为“集群总线端口”,是在客户端端口号上加10000,比如16379,用于节点之间通过二进制协议通讯。各节点通过集群总线检测宕机节点、更新配置、故障转移验证等。客户端只能使用客户端端口,不能使用集群总线端口。请确保你的防火墙允许打开这两个端口,否则redis集群没法工作。客户端端口和集群总线端口之间的差值是固定的,集群总线端口比客户端端口高10000。

注意,关于集群的2个端口:

  • 客户端端口(一般是6379)需要对所有客户端和集群节点开放,因为集群节点需要通过该端口转移数据。
  • 集群总线端口(一般是16379)只需对集群中的所有节点开放

这2个端口必须打开,否则集群没法正常工作。

集群节点之间通过集群总线端口交互数据,使用的协议不同于客户端的协议,是二进制协议,这可以减少带宽和处理时间。

Redis集群数据的分片

Redis集群不是使用一致性哈希,而是使用哈希槽。整个redis集群有16384个哈希槽,决定一个key应该分配到那个槽的算法是:计算该key的CRC16结果再模16834。

集群中的每个节点负责一部分哈希槽,比如集群中有3个节点,则:

  • 节点A存储的哈希槽范围是:0 – 5500
  • 节点B存储的哈希槽范围是:5501 – 11000
  • 节点C存储的哈希槽范围是:11001 – 16384

这样的分布方式方便节点的添加和删除。比如,需要新增一个节点D,只需要把A、B、C中的部分哈希槽数据移到D节点。同样,如果希望在集群中删除A节点,只需要把A节点的哈希槽的数据移到B和C节点,当A节点的数据全部被移走后,A节点就可以完全从集群中删除。

因为把哈希槽从一个节点移到另一个节点是不需要停机的,所以,增加或删除节点,或更改节点上的哈希槽,也是不需要停机的。

如果多个key都属于一个哈希槽,集群支持通过一个命令(或事务, 或lua脚本)同时操作这些key。通过“哈希标签”的概念,用户可以让多个key分配到同一个哈希槽。哈希标签在集群详细文档中有描述,这里做个简单介绍:如果key含有大括号”{}”,则只有大括号中的字符串会参与哈希,比如”this{foo}”和”another{foo}”这2个key会分配到同一个哈希槽,所以可以在一个命令中同时操作他们。

Redis集群的主从模式

为了保证在部分节点故障或网络不通时集群依然能正常工作,集群使用了主从模型,每个哈希槽有一(主节点)到N个副本(N-1个从节点)。在我们刚才的集群例子中,有A,B,C三个节点,如果B节点故障集群就不能正常工作了,因为B节点中的哈希槽数据没法操作。但是,如果我们给每一个节点都增加一个从节点,就变成了:A,B,C三个节点是主节点,A1, B1, C1 分别是他们的从节点,当B节点宕机时,我们的集群也能正常运作。B1节点是B节点的副本,如果B节点故障,集群会提升B1为主节点,从而让集群继续正常工作。但是,如果B和B1同时故障,集群就不能继续工作了。

Redis集群的一致性保证

Redis集群不能保证强一致性。一些已经向客户端确认写成功的操作,会在某些不确定的情况下丢失。

产生写操作丢失的第一个原因,是因为主从节点之间使用了异步的方式来同步数据。

一个写操作是这样一个流程:

    1. 1)客户端向主节点B发起写的操作
    1. 2)主节点B回应客户端写操作成功
    1. 3)主节点B向它的从节点B1,B2,B3同步该写操作

从上面的流程可以看出来,主节点B并没有等从节点B1,B2,B3写完之后再回复客户端这次操作的结果。所以,如果主节点B在通知客户端写操作成功之后,但同步给从节点之前,主节点B故障了,其中一个没有收到该写操作的从节点会晋升成主节点,该写操作就这样永远丢失了。

就像传统的数据库,在不涉及到分布式的情况下,它每秒写回磁盘。为了提高一致性,可以在写盘完成之后再回复客户端,但这样就要损失性能。这种方式就等于Redis集群使用同步复制的方式。

基本上,在性能和一致性之间,需要一个权衡。

如果真的需要,Redis集群支持同步复制的方式,通过WAIT指令来实现,这可以让丢失写操作的可能性降到很低。但就算使用了同步复制的方式,Redis集群依然不是强一致性的,在某些复杂的情况下,比如从节点在与主节点失去连接之后被选为主节点,不一致性还是会发生。

这种不一致性发生的情况是这样的,当客户端与少数的节点(至少含有一个主节点)网络联通,但他们与其他大多数节点网络不通。比如6个节点,A,B,C是主节点,A1,B1,C1分别是他们的从节点,一个客户端称之为Z1。

当网络出问题时,他们被分成2组网络,组内网络联通,但2组之间的网络不通,假设A,C,A1,B1,C1彼此之间是联通的,另一边,B和Z1的网络是联通的。Z1可以继续往B发起写操作,B也接受Z1的写操作。当网络恢复时,如果这个时间间隔足够短,集群仍然能继续正常工作。如果时间比较长,以致B1在大多数的这边被选为主节点,那刚才Z1发给B的写操作都将丢失。

注意,Z1给B发送写操作是有一个限制的,如果时间长度达到了大多数节点那边可以选出一个新的主节点时,少数这边的所有主节点都不接受写操作。

这个时间的配置,称之为节点超时(node timeout),对集群来说非常重要,当达到了这个节点超时的时间之后,主节点被认为已经宕机,可以用它的一个从节点来代替。同样,在节点超时时,如果主节点依然不能联系到其他主节点,它将进入错误状态,不再接受写操作。

Redis集群参数配置

  • cluster-enabled <yes/no>: 如果配置”yes”则开启集群功能,此redis实例作为集群的一个节点,否则,它是一个普通的单一的redis实例。
  • cluster-config-file <filename>: 注意:虽然此配置的名字叫“集群配置文件”,但是此配置文件不能人工编辑,它是集群节点自动维护的文件,主要用于记录集群中有哪些节点、他们的状态以及一些持久化参数等,方便在重启时恢复这些状态。通常是在收到请求之后这个文件就会被更新。
  • cluster-node-timeout <milliseconds>: 这是集群中的节点能够失联的最大时间,超过这个时间,该节点就会被认为故障。如果主节点超过这个时间还是不可达,则用它的从节点将启动故障迁移,升级成主节点。注意,任何一个节点在这个时间之内如果还是没有连上大部分的主节点,则此节点将停止接收任何请求。
  • cluster-slave-validity-factor <factor>: 如果设置成0,则无论从节点与主节点失联多久,从节点都会尝试升级成主节点。如果设置成正数,则cluster-node-timeout乘以cluster-slave-validity-factor得到的时间,是从节点与主节点失联后,此从节点数据有效的最长时间,超过这个时间,从节点不会启动故障迁移。假设cluster-node-timeout=5,cluster-slave-validity-factor=10,则如果从节点跟主节点失联超过50秒,此从节点不能成为主节点。注意,如果此参数配置为非0,将可能出现由于某主节点失联却没有从节点能顶上的情况,从而导致集群不能正常工作,在这种情况下,只有等到原来的主节点重新回归到集群,集群才恢复运作。
  • cluster-migration-barrier <count>:主节点需要的最小从节点数,只有达到这个数,主节点失败时,它从节点才会进行迁移。更详细介绍可以看本教程后面关于副本迁移到部分。
  • cluster-require-full-coverage <yes/no>:在部分key所在的节点不可用时,如果此参数设置为”yes”(默认值), 则整个集群停止接受操作;如果此参数设置为”no”,则集群依然为可达节点上的key提供读操作。

创建和使用Redis集群

要创建集群,首先需要以集群模式运行的空redis实例。也就说,以普通模式启动的redis是不能作为集群的节点的,需要以集群模式启动的redis实例才能有集群节点的特性、支持集群的指令,成为集群的节点。

下面是最小的redis集群的配置文件:


port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

开启集群模式只需打开cluster-enabled配置项即可。每一个redis实例都包含一个配置文件,默认是nodes.conf,用于存储此节点的一些配置信息。这个配置文件由redis集群的节点自行创建和更新,不能由人手动地去修改。

一个最小的集群需要最少3个主节点。第一次测试,强烈建议你配置6个节点:3个主节点和3个从节点。

开始测试,步骤如下:先进入新的目录,以redis实例的端口为目录名,创建目录,我们将在这些目录里运行我们的实例。

类似这样:

mkdir cluster-test
cd cluster-test
mkdir 7000 7001 7002 7003 7004 7005

在7000-7005的每个目录中创建配置文件redis.conf,内容就用上面的最简配置做模板,注意修改端口号,改为跟目录一致的端口。

把你的redis服务器(用GitHub中的不稳定分支的最新的代码编译来)拷贝到cluster-test目录,然后打开6个终端页准备测试。

在每个终端启动一个redis实例,指令类似这样:

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可以改,端口可以改,但此ID不能改,直到这个节点离开集群。这个ID称之为节点ID(Node ID)。

创建集群

现在6个实例已经运行起来了,我们需要给节点写一些有意义的配置来创建集群。redis集群的命令工具redis-trib可以让我们创建集群变得非常简单。redis-trib是一个用ruby写的脚本,用于给各节点发指令创建集群、检查集群状态或给集群重新分片等。redis-trib在Redis源码的src目录下,需要gem redis来运行redis-trib。

gem install redis

创建集群只需输入指令:

./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,因为我们需要创建一个新的集群。选项”–replicas 1”表示每个主节点需要一个从节点。其他参数就是需要加入这个集群的redis实例的地址。

我们创建的集群有3个主节点和3个从节点。

redis-trib会给你一些配置建议,输入yes表示接受。集群会被配置并彼此连接好,意思是各节点实例被引导彼此通话并最终形成集群。最后,如果一切顺利,会看到类似下面的信息:

[OK] All 16384 slots covered

这表示,16384个哈希槽都被主节点正常服务着。

使用create-cluster脚本创建redis集群

如果你不想像上面那样,单独的手工配置各节点的方式来创建集群,还有一个更简单的系统(当然也没法了解到集群运作的一些细节)。

在utils/create-cluster目录下,有一个名为create-cluster的bash脚本。如果需要启动一个有3个主节点和3个从节点的集群,只需要输入以下指令

1. create-cluster start
2. create-cluster create

在步骤2,当redis-trib要你接受集群的布局时,输入”yes”。

现在你可以跟集群交互,第一个节点的起始端口默认是30001。当你完成后,停止集群用如下指令:

1. create-cluster stop.

请查看目录下的README,它有详细的介绍如何使用此脚本。

试用一下集群

可以用上面提供的客户端或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"

注意:如果你用脚本来创建的集群,你的redis可能监听在不同的端口,默认是从30001开始。

redis-cli利用集群的任意节点会告知客户端正确节点的特性,实现了集群客户端的最基础功能。实现得比较严谨的客户端可以缓存哈希槽到节点的映射关系,让客户端直接连接到正确的节点,只有集群的节点配置有更新时才刷新缓存,比如发生了故障迁移,或者管理员增加或减少了节点等。

重新分片

重新分片简单的说就是把哈希槽从一些节点移动到另外一些节点。重新分片可以像创建集群一样,使用redis-trib来完成。

开始重新分片,输入以下指令:

./redis-trib.rb reshard 127.0.0.1:7000

你只需要指定集群中的一个节点,redis-trib会自动找到集群中的其他节点。

目前,redis-trib只支持管理员操作,不能够说:移动50%的哈希槽从这个节点到那个节点。它以问问题的方式开始。第一个问题是,你需要重新分片多少个哈希槽:

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

由于我们之前的脚本一直在运行,而且没有用sleep调用,这时候应该已经插入了比较多的key了。我们可以尝试给1000个哈希槽重新分片。

然后,redis-trib需要知道我们要把这1000个哈希槽移动到哪个节点去,也就是接受这1000个哈希槽的节点。我想用127.0.0.1:7000这个节点。需要用节点ID来告知redis-trib是哪个节点。redis-trib已经在屏幕上列出了所有的节点和他们的ID。也可以通过以下命令找到指定节点的ID:

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

好了,我的目标节点是97a3a64667477371c4479320d683e4c8db5858b1。

现在redis-trib会问:你想从哪些节点中挪走这些哈希槽呢?我输入all,会从其他的主节点中挪走哈希槽。

在输入最后确认之后,redis-trib在屏幕上会输出每一个哈希槽将从哪个节点转移到哪个节点。每实际移动一个key屏幕就会打印一个点。

在重新分片的过程中,你可以看到,你刚才运行的脚本不受影响,你甚至可以在重新分片的过程中,反复的重新运行该例子脚本。

在重新分片结束后,你可以检查集群的当前状态是否正常,运行下面的命令:

./redis-trib.rb check 127.0.0.1:7000

所有的哈希槽都存在,这时候127.0.0.1:7000的主节点有多一点的哈希槽,有大概6461个。

脚本化重新分片

重新分片可以不用以交互的方式进行,使用下面的指令可以自动执行:

./redis-trib.rb reshard --from <node-id> --to <node-id> --slots <number of slots> --yes <host>:<port>

如果你想要经常的重新分片,可以使用上面的指令自动分片,但是目前redis-trib脚本不会根据节点上的key的分布来做负载均衡、智能地迁移哈希槽。这个特性在将来我们会添加的。

测试故障迁移

注意:测试过程中,请让上面的一致性测试的应用程序一直运行中。
为了触发故障迁移,最简单的办法是让一个进程宕机,在我们的用例中,就是让其中一个主节点进程宕机。
我们可以用下面的指令区分集群节点:

$ 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

所以,7000,7001,7002是主节点,我们要使7002当机,使用DEBUG SEGFAULT指令:

$ redis-cli -p 7002 debug segfault
Error: Server closed the connection
现在,我们看看刚才那个一致性检测器的例子输出了什么:
18849 R (0 err) | 18849 W (0 err) |
23151 R (0 err) | 23151 W (0 err) |
27302 R (0 err) | 27302 W (0 err) |

... many error warnings here ...

29659 R (578 err) | 29660 W (577 err) |
33749 R (578 err) | 33750 W (577 err) |
37918 R (578 err) | 37919 W (577 err) |
42077 R (578 err) | 42078 W (577 err) |

我们看到,例子显示有578个读失败和577个写失败,但没有不一致性产生。我们前面的章节提到过,redis集群不是强一致性的,由于它异步复制数据到从节点,可能会在主节点失败的情况下导致数据丢失,但是上面的例子显示没有不一致性产生,为什么呢?因为主节点响应客户端后马上同步数据给从节点,这几乎是同时的,这里的时间差非常小,只有在这个非常小的时间差中主节点故障,才会发生不一致性。尽管发生的可能性很小,不代表它不可能发生,redis集群依然不是强一致性的。
现在我们看看当该节点故障之后,集群做了什么(注意,我已经重启了该故障的节点,它已经重新连上集群,并成为了从节点):

$ redis-cli -p 7000 cluster nodes
3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385503418521 0 connected
a211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385503419023 0 connected
97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422
3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385503419023 3 connected 11423-16383
3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385503417005 0 connected 5960-10921
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385503418016 3 connected

现在,主节点的端口变成了:7000,7001,7005(之前主节点是7002的,现在变成主节点是7005了)。
“cluster nodes”指令的输出看起来蛮吓人的,其实很简单,它的每列意义如下:

*节点ID
*IP:端口
*标记位:主节点,从节点,自己,失败。。。
*如果是从节点,则接下来是它的主节点的ID
*最后一次发送ping依然等待响应的时间
*最后一次收到pong的时间
*上次更新此配置的时间
*节点的连接状态
*存储的哈希槽

手动故障转移

有时候,手动故障转移是非常有用的,它不会给主节点带来任何问题,比如,需要升级某个主节点的redis进程,可以先通过手动故障转移使之成为从节点,让升级对集群可用性的影响达到最低。
redis集群支持通过指令”CLUSTER FAILOVER”产生故障转移,但需要在被失效的主节点的一个从节点上执行该命令。
相对于真的主节点宕机,手动故障转移是比较安全的,它可以避免数据丢失,当新的主节点复制完所有数据之后,会让客户端从原来的主节点重定向到新的主节点。
下面是在其中一个从节点上执行了cluster failover指令之后看到的一些日志:

# 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.

简单的说:客户端停止连接被故障转移的原主节点;同时该原主节点把还没同步的复制集同步给从节点;当从节点收到所有复制集之后,故障转移开始,原来主节点被通知配置更新,主节点更换了;客户端被重定向到新的主节点。

新增新的节点

新增一个节点,就增加一个空的节点到集群。有两种情况:如果新增的是主节点,则是从集群的其他节点中转移部分数据给它;如果新增的是从节点,则告诉它从一个已知的节点中同步复制集。

我们2种情况都试试。首先是新增一个新的主节点到集群中。
两种情况,都是需要先加入一个空的节点到集群中

鉴于我们前面已经启动了6个节点,端口号7000-7005已经用了,新增节点的端口号就用7006吧。新增一个新的空节点,就跟上面启动前面6个节点的步骤一样(记得改配置文件的端口号):

    1. *在终端打开一个新的页面
    1. *进入到cluster-test目录
    1. *创建名为“7006”的目录
    1. *在该目录下创建redis.conf文件,内容跟其他节点的内容一致,只是端口号改成7006.
    1. *最后启动它:../redis-server ./redis.conf

现在该节点应该运行起来了。
现在,我们使用redis-trib来增加一个新节点到集群中:

./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000

使用add-node指令来新增节点,第一个地址是需要新增的节点地址,第二个地址是集群中任意一个节点地址。
redis-trib脚本只是给发送CLUSTER MEET消息给节点,这也可以手动地通过客户端发送,但redis-trib在发送之前会检查集群的状态,所以,还是用redis-trib脚本来操作集群会比较好。
现在我们可以连上新的节点,看看它是不是已经加入集群了:

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

虽然现在此新节点已经连到集群,并且可以重定向客户端到正确的集群节点了,但是它跟集群的其他主节点有个不同的地方:
*它没有数据,因为没有分配哈希槽给它
*因为它是一个没有哈希槽的主节点,当一个从节点需要被选举成新主节点时,它没有参与权

可以通过redis-trib的重新分片指令来给新节点增加哈希槽。由于前面已经介绍过如何重新分片了,这里就不做详细介绍。

添加一个从节点

新增从节点有两种方法,第一个是使用上面的redis-trib脚本,增–slave选项,类似这样:

./redis-trib.rb add-node --slave 127.0.0.1:7006 127.0.0.1:7000

注意到上面的命令行跟我们加主节点的命令行类似,所以没有没有指定新增的从节点的主节点是哪个,这时候redis-trib会在拥有最少从节点的主节点中随机选一个作为新增节点的主节点。
当然也可以通过如下的命令指定新增从节点的主节点:

./redis-trib.rb add-node --slave --master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7006 127.0.0.1:7000

用上面的指令,我们可以指定新的从节点是那个主节点的副本集。

另一个方法,先把新节点以主节点的形式加入到集群,然后再用“CLUSTER REPLICATE”指令把它变为从节点。这个方式也适用于给从节点更换主节点。
比如,已有主节点127.0.0.1:7005,它存储的哈希槽范围是11423-16383,节点ID为3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e,我们希望给它新增从节点。首先用之前的方法新增一个空的主节点,然后连上该新节点,发送如下指令:

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…有2个从节点,分别是原来的运行在7002端口的节点和刚刚新增的7006端口的节点。

删除节点

使用redis-trib的指令”del-node”可以删除节点:

./redis-trib del-node 127.0.0.1:7000 `node-id`

第一个参数是集群的任意一个节点,第二个参数是需要删除的节点的ID。
同样的方法可以删除主节点,但是在删除之前,需要通过重新分片把数据都移走。
另一个删除主节点的方式是通过手动故障转移,让它的其中一个从节点升级成主节点后再把此节点删除。但这样并不会减少集群的主节点数,如果需要减少主节点数,重新分片在所难免。

复制集迁移

在redis集群中,可以通过如下指令,在任意时间给从节点更换主节点:

CLUSTER REPLICATE <master-node-id>

有一种特殊的场景:系统自动地更改复制集的主节点,而不是要管理员手动处理。这种自动重新配置从节点的情景叫副本集迁移(replicas migration),它可以增加redis集群的健壮性。

注意:你可以通过《Redis Cluster Specification》了解更多详细的内容,这里只是对它的简单介绍以及它的用处。

假如一个每个主节点只有一个从节点的集群,在一个主节点和从节点同时故障的情况下,集群将不能继续工作,因为已经故障的节点中存储的哈希槽数据已经没法读写。虽然网络断开很可能会让一大批节点同时被隔离,但是还有很多其他情况会导致节点故障,比如硬件或者软件的故障导致一个节点宕机,也是非常重要的导致节点故障的原因,这种情况一般不会所有节点同时故障。比如,集群中每个主节点都有一个从节点,在4点的时候一个从节点被kill,该主节点在6点被kill。这样依然会导致集群不能工作。

为了增强系统的可用性,可以给每个主节点再增加一个从节点,但这样做是比较昂贵的。复制集迁移可以让我们只给部分主节点增加多些从节点。比如有10个主节点,每个主节点有1个从节点,总共20个节点,然后可以再增加一些从节点(比如3个从节点)到一些主节点,这样就有部分主节点的从节点数超过1。

当一个主节点没有从节点时,如果集群中存在一个主节点有多个从节点时,复制集迁移机制在这些多个从节点中找一个节点,给没有从节点的主节点做复制集。所以当4点钟一个从节点宕机,另一个从节点将会代替它成为该主节点的从节点;然后在5点钟主节点宕机时还有一个从节点可以升级成主节点,这样集群可以继续运行。

所以,简单的说副本集迁移就是:
*集群会找到拥有最多从节点的主节点,在它的从节点中挑选一个,进行复制集迁移
*为了让复制集迁移生效,只需要在集群中多加几个从节点,随便加到哪个主节点都可以
*关于复制集迁移,有一个配置参数叫“cluster-migration-barrier”,在集群的样板配置文件中有详细说明,需要了解清楚

升级集群中的节点

升级从节点非常简单,只要停止它再重启更新过的版本即可。如果客户端连到了从节点,在该节点不可用时,客户端需要重连到另外可用的从节点上。

升级主点则相对复杂,下面是推荐的流程:
1. 使用CLUSTER FAILOVER指令触发手动故障转移,让主节点变成从节点
2. 等到主节点成为从节点
3.升级该从节点
4. 如果你想让升级过的节点重新变成主节点,则再次触发手动故障转移,让它变成新的主节点。

用这样的步骤,一个个的升级所有节点。

迁移到redis集群

用户需要把redis的数据迁移到redis集群,原来的数据可能是只有一个主节点,也可能是用已有的方式分片过,key被存储在N个几节点中。

上面2中情况都很容易迁移,特别重要的细节是是否使用了多个key以及是如何使用多个key的。下面是3种不同的情况:
1. 没有操作多个key(包括操作多个key的指令、事务、lua脚本)。所有key都是独立操作的.
2. 操作了多个key(包括操作多个key的指令、事务、lua脚本),但这些key都有相同的哈希标签,比如这些被同时操作的key:SUNION{user:1000}.foo {user:1000}.bar
3. 操作了多个key(包括操作多个key的指令、事务、lua脚本),这些key没有特别处理,也没有相同标签。

第三种情况redis集群没法处理,需要修改应用程序,不要使用多个key,或者给这些key加上相同的哈希标签。

第一和第二种情况可以处理,而且他们的处理方式一样。

假设你已有的数据被分成N个主节点存储(当N=1时,就是没有分片的情况),要把数据迁移到redis集群,需要执行下面几个步骤:
1. 停止你的客户端。目前没有自动在线迁移到redis集群的方法。你可以自己策划如何让你的应用程序支持在线迁移。
2. 使用BGREWRITEAOF指令让所有主节点产生AOF文件,并且等待这些文件创建完成。
3. 把这些AOF文件保存下来,分别命名为aof-1, aof-2, ..aof-N,如果需要,可以停止原来的redis实例(对于非虚拟化部署,需要重用这台电脑来说,把旧进程停掉很有帮助)。
4. 创建N个主节点+0个从节点的redis集群。晚些时候再添加从节点。请确认所有节点都开启了appendonly的配置。
5. 停止集群的所有节点,然后用刚才保存的AOF文件,代替每个节点的AOF文件,aof-1给第一个节点,aof-2给第二个节点,以此类推。
6. 重启所有节点,这些节点可能会提示说根据配置有些key不应该存储在这个节点。
7. 使用redis-trib fix指令,让集群自动根据哈希槽迁移数据
8. 使用redis-trib check指令确保你的集群是正常的
9. 让你的客户端使用redis集群客户端库,并重启它。

还有一个方法可以从已有的redis实例中导入数据到redis集群,使用redis-trib import指令。该指令会把源实例中的数据都删除,并把数据写入事先部署好的集群中。需要注意的是,如果你的源实例使用的是redis2.8版本,这个导入过程可能会比较长,因为2.8版本没有实现数据迁移的连接缓存,所以最好把源实例的redis版本先升级到3.x的版本。

时间: 2024-11-08 09:27:48

Redis集群知识解析的相关文章

redis集群讨论

一.生产应用场景 二.存储架构演变 三.应用最佳实践 四.运维经验总结 第1.2节:介绍redis cluster在唯品会的生产应用场景,以及存储架构的演变.第3节:redis cluster的稳定性,应用成熟度,踩到过那些坑,如何解决这些问题?这部分是大家比较关心的内容.第4节:简单介绍大规模运营的一些经验,包括部署.监控.管理以及redis工具开发. 一.生产应用场景 1.业务范围 redis cluster在唯品会主要应用于后端业务,用作内存存储服务.主要大数据实时推荐/ETL.风控.营销

redis集群实现(一)集群架构与初始化

redis是一个高可用.高性能.高可扩展性的基于内存也支持持久化存储的kv存储数据库,redis相比较于之前的kv存储memcached而言,不但支持的value类型大大增加,并且还支持数据的持久化,弥补了memcached的不能持久化的缺点,但是在3.0之前的redis并不支持集群功能,这也是redis在3.0之前不能被大量部署的一个原因,但是由于3.0以后的redis支持了集群功能,redis就开始大量的替代之前的memcached,今天我从源代码层次学习下redis是怎么实现集群功能的.

2.Redis集群环境搭建

转载请出自出处:http://www.cnblogs.com/hd3013779515/ 一.基本概念 1.redis集群是一个可以在多个节点之间进行数据共享的设施.redis集群提供了以下两个好处1.1 将数据自动切分(split)到多个节点1.2 当集群中的某一个节点故障时,redis还可以继续处理客户端的请求. 2.一个 Redis 集群包含 16384 个哈希槽(hash slot),数据库中的每个数据都属于这16384个哈希槽中的一个.集群使用公式 CRC16(key) % 16384

架构设计:系统存储(18)——Redis集群方案:高性能

1.概述 通过上一篇文章(<架构设计:系统存储(17)--Redis集群方案:高可用>)的内容,Redis主从复制的基本功能和进行Redis高可用集群监控的Sentinel基本功能基本呈现给了读者.虽然本人并不清楚上一篇根据笔者实际工作经验所撰写的文章有什么重大问题,导致那么多朋友集体点踩而且截止目前又没有任何人愿意为笔者指出这些问题,但是这不会影响笔者继续学习.总结技术知识的热情.从这篇文章开始我们一起来讨论Redis中两种高性能集群方案,并且在讨论过程中将上一篇文章介绍的高可用集群方案结合

tomcat+nginx+redis集群试验

Nginx负载平衡 + Tomcat + 会话存储Redis配置要点 使用Nginx作为Tomcat的负载平衡器,Tomcat的会话Session数据存储在Redis,能够实现0当机的7x24 运营效果.因为将会话存储在Redis中,因此Nginx就不必配置成stick粘粘某个Tomcat方式,这样才能真正实现后台多个Tomcat负载平 衡,用户请求能够发往任何一个tomcat主机,当我们需要部署新应用代码时,只要停止任何一台tomcat,所有当前在线用户都会导向到运行中的 tomcat实例,因

redis集群部署之codis 维护脚本

搞了几天redis cluster codis 的部署安装,测试,架构优化,配合研发应用整合,这里记一些心得! 背景需求: 之前多个业务都在应用到redis库,各业务独立占用主从两台服务器,硬件资源利用不合理,主从架构冗余度不高,主redis故障的话,从redis恢复需要时间,降低业务的可用性, 所以调研测试部署了基于codis的redis集群. 官方地址 部署文档 参考这里redis cluster安装部署 维护管理 了解过codis的同志都知道codis集群组件服务启动有一定顺序的,而且基本

java操作redis数据库实例(redis集群)

1.配置redis集群 <?xml version="1.0" encoding="UTF-8"?> <redisCluster> <!--userRoute --> <clusterGroup name="userRoute" selectdb="1"> <server host="10.177.129.16" port="6379"

深入剖析Redis系列: Redis集群模式搭建与原理详解

前言 在 Redis 3.0 之前,使用 哨兵(sentinel)机制来监控各个节点之间的状态.Redis Cluster 是 Redis 的 分布式解决方案,在 3.0 版本正式推出,有效地解决了 Redis 在 分布式 方面的需求.当遇到 单机内存.并发.流量 等瓶颈时,可以采用 Cluster 架构方案达到 负载均衡 的目的. 本文将从 集群方案.数据分布.搭建集群.节点通信.集群伸缩.请求路由.故障转移.集群运维 等几个方面介绍 Redis Cluster. 正文 1. Redis集群方

支撑微博亿级社交平台,小白也能玩转Redis集群(原理篇)

Redis作为一款性能优异的内存数据库,支撑着微博亿级社交平台,也成为很多互联网公司的标配.这里将以Redis Cluster集群为核心,基于最新的Redis5版本,从原理再到实战,玩转Redis集群 常见Redis集群方案 在介绍Redis Cluster集群方案之前,为了方便对比,先简单了解一下业界常见的Redis集群方案: 1 基于客户端分片 Redis Sharding是Redis Cluster出来之前,业界普遍使用的多Redis实例集群方法.其主要思想是基于哈希算法,根据Redis数