Redis Cluster一窥

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值所在的槽重定向至对应的节点。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容