redis知识库@IT·互联网互联网科技

Redis Cluster一窥

2016-09-05  本文已影响632人  大蟒传奇
Redis Cluster

什么是Redis Cluster

Redis Cluster让多个Redis节点以集群的方式存储数据,数据会自动分片保存。
在提供分区的情况下,Redis Cluster可以保证一定程度的可用性,即在一个节点挂掉的情况下,整个集群依然可以正常提供服务。但是出现大多数master节点都挂的情况时,整个集群也会一起挂掉。

综上,Redis Cluster提供了两个特性:
1、数据的自动分区存储
2、高可用性。即部分节点挂掉不影响集群的可用性。

TCP端口

每个Redis Cluster节点会占用两个TCP端口,一个监听客户端的请求,默认是6379,另外一个在前一个端口加上10000,比如16379,来监听数据的请求。

节点和节点之间会监听第二个端口,用一套二进制协议来通信。
节点之间会通过套协议来进行失败检测,配置更新,failover认证等等。
为了保证节点之间正常的访问,需要注意防火墙的配置。

数据分片

Redis 集群没有并使用传统的一致性哈希来分配数据,而是采用另外一种叫做哈希槽的方式来分配。
Redis Cluster默认分配了16384个槽,在分配的时候,会采用根据CRC16(key) % 16384的计算结果来将数据分配到节点上。
Redis Cluster中的每个节点会负责存储数据槽的一个子集,假设有三个节点

  1. A节点存储0到5500
  2. B节点存储5501到11000
  3. C节点存储11001到16383
    根据上面的公式计算的结果,如果大于0小于5500,那么数据将被存储到A节点上。

这样的特性使得增加或者删除节点非常得容易。假如要加入节点D,只需要将A,B,C的部分槽转移到D上;同理,要删除C,只需要将C的槽转移到A和B上,当C节点的数据被转移,清空后,就可以删除C节点了。

在移动哈希槽的时候,不需要停机维护。

如果一个指令涉及到多个key时,只要这些key属于同一个槽,Redis Cluster允许同时操作他们。用户可以通过hash tags强制将多个key归到一个槽中。

主从模式

Redis Cluster支持主从模式,从节点会保存主节点的全部数据
在上面的例子中,假设B节点挂了,那么5501-11000的槽将不能够提供服务。
现在给上面的节点添加一个从节点,那么我们将有A,B,C,A1,B1,C1六个节点。
当B节点挂掉,B1将会升级为主节点,系统的可用性将不受到影响。
但是,如果B和B1节点同时挂掉的话,集群依然会挂掉。

数据一致性

Redis Cluster不会保证数据的强一致性。在部分场景下可能会出现数据丢失的现象。
会丢数据的第一个原因是Redis Cluster中主节点异步向从节点拷贝数据。
在客户写入数据时,会经过下面的过程。

  1. 写到主节点B
  2. 主节点B返回"OK"
  3. 主节点将数据复制到从节点B1,B2,B3

可以看到,主节点B在向客户返回"OK"的之前,并不会等待B1,B2,B3确认写入的回应。这样主要是为了性能的考虑,不能让客户等待太长时间。
假设一种情况,client写入数据,节点B确认这次写入,但是在步骤3之前挂掉了,B1成为了新的主节点。那么这次写入就永远失效了。

可以看到Redis的高性能也是需要一定的代价的。Redis Cluster采取的方案实际上是数据一致性和高性能的折衷。

Redis Cluster支持同步写入,见WAIT操作,这样会降低丢数据的可能性,但是在复杂的情况下,还是可能出现从节点没有接到写入,然后成为主节点。

假设有A,B,C,A1,B1,C1六个节点,三主三从,现在有一个客户Z1。
现在网络出现隔离,A,C,A1,B1,C1之间是联通的;Z1和B是联通的。Z1向B写入成功,如果网络很快恢复,那么一切正常,但是没有很快恢复的情况下,B1会成为新的主节点,Z1向B的写入就丢失了。

上面说了这么多,总结一句话,
<strong><big>即使有了集群,也不能将Redis用做高可靠性的存储。</big></strong>

部署相关

配置选项

  1. cluster-enabled<yes/no> 如果设置为yes,redis实例会开启cluster模式;如果设置为no,standalone模式
  2. cluster-config-file<filename>
  3. cluster-node-timeout<milliseconds> 一个节点被判定为挂掉的最大时间
  4. **cluster-slave-validity-factor <factor>
    **:如果设置为0,从节点会一直failover主节点
  5. **cluster-migration-barrier <count>
    **:主节点对应最小从节点的个数
  6. **cluster-require-full-coverage <yes/no>
    **:如果设为yes,一旦部分哈希槽不能访问到,集群会停止接受写入;如果设为no,即使在部分哈希槽不能被访问到的情况下,集群依然后接受写入。

创建集群

我用的是mac系统,其他类unix系统应该差不多。
先装一些工具软件,下载一下源码。redis的使用的第三方库的源码都包含在项目中了,这简直是编译安装的福音。

brew install ruby
brew install gem
gem install redis
git clone https://github.com/antirez/redis
cd redis
make

然后进入目录utils/create-cluster,执行命令

./create-cluster start

看到输出,建立了6个单独的实例。

Starting 30001
Starting 30002
Starting 30003
Starting 30004
Starting 30005
Starting 30006

执行命令

./create-cluster create

看到输出

>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
127.0.0.1:30001
127.0.0.1:30002
127.0.0.1:30003
Adding replica 127.0.0.1:30004 to 127.0.0.1:30001
Adding replica 127.0.0.1:30005 to 127.0.0.1:30002
Adding replica 127.0.0.1:30006 to 127.0.0.1:30003
M: 6687e151e76830f8544ab9527bbb65b8d9063de4 127.0.0.1:30001
   slots:0-5460 (5461 slots) master
M: ea2e8ab4d48f5b0a1527dc0ac6d4f14e80f6aca0 127.0.0.1:30002
   slots:5461-10922 (5462 slots) master
M: 4a808670aae774e49343aac8526b641d5aa79de8 127.0.0.1:30003
   slots:10923-16383 (5461 slots) master
S: cee189bce870d87c318840a1e6a2d5045806acba 127.0.0.1:30004
   replicates 6687e151e76830f8544ab9527bbb65b8d9063de4
S: 68838ae12c64ccfccfe55b607a99454678d35ea4 127.0.0.1:30005
   replicates ea2e8ab4d48f5b0a1527dc0ac6d4f14e80f6aca0
S: 075ced4cef8e8ec06076b5193efb81812d72dc1b 127.0.0.1:30006
   replicates 4a808670aae774e49343aac8526b641d5aa79de8
Can I set the above configuration? (type 'yes' to accept): yes

输入yes后,建立集群
可以看到30001,30002,30003作为主节点
30004,30005,30006分别作为其从节点。

执行脚本

来看一下代码

start

在执行start指令的时候,实际上是执行了下面的操作。

../../src/redis-server --port $PORT --cluster-enabled yes --cluster-config-file nodes-${PORT}.conf --cluster-node-timeout $TIMEOUT --appendonly yes --appendfilename appendonly-${PORT}.aof --dbfilename dump-${PORT}.rdb --logfile ${PORT}.log --daemonize yes

可以看到##配置选项里面项目都设置了一个值,来看一下--cluster-config-file这个配置里的文件长啥样。挑一个主节点看看

cat nodes-30003.conf
ea2e8ab4d48f5b0a1527dc0ac6d4f14e80f6aca0 127.0.0.1:30002 master - 0 1473069793428 2 connected 5461-10922
4a808670aae774e49343aac8526b641d5aa79de8 127.0.0.1:30003 master - 0 1473069793529 3 connected 10923-16383
075ced4cef8e8ec06076b5193efb81812d72dc1b 127.0.0.1:30006 slave 4a808670aae774e49343aac8526b641d5aa79de8 0 1473069793428 6 connected
cee189bce870d87c318840a1e6a2d5045806acba 127.0.0.1:30004 slave 6687e151e76830f8544ab9527bbb65b8d9063de4 0 1473069793428 4 connected
6687e151e76830f8544ab9527bbb65b8d9063de4 127.0.0.1:30001 myself,master - 0 0 1 connected 0-5460
68838ae12c64ccfccfe55b607a99454678d35ea4 127.0.0.1:30005 slave ea2e8ab4d48f5b0a1527dc0ac6d4f14e80f6aca0 0 1473069793024 5 connected
vars currentEpoch 6 lastVoteEpoch 0

再挑一个从节点看看

cat nodes-30006.conf
4a808670aae774e49343aac8526b641d5aa79de8 127.0.0.1:30003 master - 0 1473069792576 3 connected 10923-16383
cee189bce870d87c318840a1e6a2d5045806acba 127.0.0.1:30004 myself,slave 6687e151e76830f8544ab9527bbb65b8d9063de4 0 0 4 connected
68838ae12c64ccfccfe55b607a99454678d35ea4 127.0.0.1:30005 slave ea2e8ab4d48f5b0a1527dc0ac6d4f14e80f6aca0 0 1473069793485 5 connected
ea2e8ab4d48f5b0a1527dc0ac6d4f14e80f6aca0 127.0.0.1:30002 master - 0 1473069793079 2 connected 5461-10922
075ced4cef8e8ec06076b5193efb81812d72dc1b 127.0.0.1:30006 slave 4a808670aae774e49343aac8526b641d5aa79de8 0 1473069792578 3 connected
6687e151e76830f8544ab9527bbb65b8d9063de4 127.0.0.1:30001 master - 0 1473069793079 1 connected 0-5460
vars currentEpoch 6 lastVoteEpoch 0

可以看到这里面维护了一个状态表,纪录了所有节点的
信息,状态等等。这个文件会将集群的信息保存下来,用于集群下次的启动,根据节点收到的信息,这个文件会经常改变,并保存。

create

执行create指令时,实际上是执行了下面的操作

../../src/redis-trib.rb create --replicas 1  127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 127.0.0.1:30006

redis-trib.rb是redis作者写的一个工具。

用用看

redis-cli -c -p 30001
127.0.0.1:30001> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:30003
OK
127.0.0.1:30003> set hello world
-> Redirected to slot [866] located at 127.0.0.1:30001
OK
127.0.0.1:30001> get foo
-> Redirected to slot [12182] located at 127.0.0.1:30003
"bar"
127.0.0.1:30003> get hellp
-> Redirected to slot [8380] located at 127.0.0.1:30002
(nil)
127.0.0.1:30002> get hello
-> Redirected to slot [866] located at 127.0.0.1:30001
"world"
127.0.0.1:30001>

可以看到,集群会根据key值所在的槽重定向至对应的节点。

上一篇 下一篇

猜你喜欢

热点阅读