缓存学习(十):分布式Redis之Redis Cluster

目录

1 数据拆分

2 Redis Cluster配置

3 集群搭建

4 集群命令

4.1 redis-cli 的集群管理模式

4.2 客户端命令

5 原理

5.1 节点通信

5.2 槽的迁移

5.2.1 集群收缩

5.2.2 集群扩容

5.3 请求路由和重定向

5.4 故障转移

6 JedisCluster

6.1 集群节点的自动发现

6.2  JedisClusterCRC16

6.3 JedisCluster对重定向的支持


1 数据拆分

副本机制和哨兵解决了Redis节点可用性的问题,同时通过读写分离部分地提升了总体性能,但是没能完全解决性能瓶颈问题:单个节点CPU处理能力和内存容量都是有极限的,不可能无限扩张,副本机制的引入在一定程度上扩展了读请求的处理能力,但一方面没有提升写请求的处理能力,另一方面还由于数据一致性的要求引入了同步的压力。

以上问题的解决方案就是数据拆分,即将数据按照一定规则分散到不同的副本集群(或称为分片)中,在读取数据时,按照相同的规则计算数据在哪个分片上,然后去目标分片查找即可。

对于关系型数据库,数据拆分一般以时间、地点等具有明确边界的数据列作为划分依据。例如按照年份划分,2016、2017、2018年的数据各存入一台服务器,好处在于划分规则简单,且这种顺序分区的形式比较容易计算位置。

但是缺点也很明显,首先这种方式很容易出现数据倾斜,以电商数据中的用户购买记录为例,购买记录的数量一般是逐年增长的,如果按年份划分的话,要么性能过剩(使用同样规格的服务器存储,则存储2016年数据的服务器性能一定相对过剩),要么失去扩展性(为每年的数据购买性能刚好的服务器,这意味着一旦数据略有伸缩,服务器可能就需要更换);另一个问题是业务需求带来的冗余,同样以购物记录说明,对于用户来说,想要看到的数据是自己在不同卖家的购买记录,即1(买家)——N(卖家),而在卖家角度,需求则完全相反,卖家希望看到的是有多少买家购买了自己的商品,即1(卖家)——N(买家),如果以买家作为划分依据,则统计卖家数据时会有很大负担,反之亦然,这种情况下,为了保证买家、卖家查询性能,只好维护两份数据,产生了两倍的请求,成本非常高。

而在Redis这种NoSQL数据库上,数据要么组织特别松散,要么特别紧密。例如要存储用户zhangsan的性别、年龄,可以有三种方式:

  • 一种是分别存储在user:zhangsan:sexual、user:zhangsan:age中;
  • 一种是将两个属性序列化为一个字符串,如{sexual:male,age:20},然后存储在一个键user:zhangsan上;
  • 还有一种就是以list的形式存储。

第一种形式其实还比较好处理,后面两种就相对麻烦一些。如果采用顺序分区,也会有关系数据库的一系列问题,解决的方案是哈希分区。

哈希分区具有以下特点:离散度较好,只要哈希规则设置的足够优秀,可以较好的解决数据倾斜问题;性能高,哈希算法可以提供O(1)级别的性能;数据分布业务无关,对所有业务均具有基本相同的性能表现;无法顺序访问,只能按照分区规则计算位置再访问。

常用的哈希分区规则有:

1)直接哈希(取余法):

即对数据的键使用哈希函数h,然后对服务器总数N取余来计算其位置,公式:h(key)%N。这种方法最大的优点就是计算非常简单,缺点是需要预先对数据量、服务器处理能力等进行规划,且伸缩性差,无论节点增加还是删除,都会对整个集群产生影响,例如原先有10个节点,某个数据经过哈希,值为16,取余后落在6号节点上,现在5号节点移除,那么读取该数据时,就会去7号节点寻找,这当然是找不到的。

2)一致性哈希:

直接哈希最大的问题就是对整个空间的划分依赖于节点数量,一旦节点数量改变,就会导致划分方式改变,从而导致原先的划分完全失效。一致性哈希则解决了这个问题,它的核心思路是,不考虑节点数量,而将整个空间划分为2^{32}块,分别标号为0~2^{32}-1,构成一个哈希环。然后将节点也同样哈希、取余,分布在环上,如下图(来自网络,侵删):

当读取或写入数据时,哈希公式变成:h(key)%2^{32},然后顺时针找到第一个节点。例如上图中,某个键的计算哈希并取余后,其值处于node1、node2之间,则按顺时针方向到node2上执行操作。假如此时将node2下线,那么影响的只是哈希值在node1和node2之间的数据,不会影响其他部分的数据。

一致性哈希存在以下问题:首先是不适合节点数量少的环境,例如上图中,如果下线一个节点,就将影响25%的数据,如果是一个100个节点的集群,那么下线一个节点只影响1%的数据,显然节点数量越多,一致性哈希的效果越好;另一个缺点是,如果有节点上线或下线,会导致顺时针方向下一个节点的负载增倍/减半,即每台服务器正常运行状态下,负载不能超过50%,这对服务器性能是极大的浪费。

3)虚拟槽

Redis Cluster使用的是该方法。该方法的思路和一致性哈希有相似之处,同样是将整个空间划分为固定的若干块,然后让每个节点负责一部分空间。

在Redis的实现中,数据空间被划分为16384个槽,数据分布算法为:crc16(key)%16384,当主节点下线时,会将其负责的槽迁移到其他节点上,迁移过程保证各节点负载相对均匀。同样以四个节点组的集群为例,当某个节点组下线后,剩下的三个节点组各负载1/3的数据,负载增大约1/3( (33%-25%)/25%),相比一致性哈希方案,有明显改进。

2 Redis Cluster配置

在redis.conf中,有一部分集群配置,在缓存学习(五):Redis安装、配置没有做介绍,在这里进行介绍。

  • cluster-enabled:是否启用集群模式
  • cluster-config-file:集群配置文件名称,该文件由节点自动维护,不需要手动编辑,且必须保证该配置在每个集群节点处都不同
  • cluster-node-timeout:集群节点间通信所允许的最大延时,超过该时间则认为节点下线,进入failover
  • cluster-slave-validity-factor:用于在failover时筛选从节点,以保证使用最新版本的数据进行恢复。在副本机制中,failover时,会以复制偏移量作为标准,尝试选取具有最大偏移量的节点进行晋升,但是偏移量大不代表数据足够新,例如进行恢复时,需要恢复最近30秒内的数据,但是最大偏移量节点的数据是40秒前的,那么恢复过来意义也不大,可以不恢复。对于一个从节点,假如它上一次和主节点正常通信发生在:cluster-node-timeout * cluster-slave-validity-factor + repl-ping-slave-period 之前,则不进行故障转移。可见在cluster-slave-validity-factor为0时,会始终进行故障转移,从而保证可用性(但是数据的一致性无法保证)。
  • cluster-migration-barrier:如果集群中某个主节点没有从节点(称为孤立主节点),则会大大降低鲁棒性,Redis可以自动地转移从节点给孤立主节点,该配置决定了转移的节点至少有额外多少个节点才会触发转移操作。该配置默认为1,即一个主节点至少有两个以上的从节点,才会转让一个从节点给孤立主节点
  • cluster-require-full-coverage:是否允许在未完成槽迁移,或者有槽未分配的情况下响应查询
  • clsuter-slave-no-failover:是否关闭自动failover,设置为yes是,将不会主动进行故障转移(可以手动进行)
  • 网络配置:用于在NAT转换网络下配置公共地址和端口,其中bus的端口一般是节点端口加上10000
    • cluster-announce-ip
    • cluster-announce-port
    • cluster-announce-bus-port

Redis集群通过总线进行节点间的数据交换,每个Redis节点都会开辟一个额外端口(Redis port加上10000)与总线进行TCP连接,以二进制形式进行数据交换。

3 集群搭建

这里以3主3从,共6个节点为例。

首先在redis.conf中开启cluster-enabled配置,并配置cluster-node-timeout

然后将redis.conf复制为6份,分别命名为redis-6379.conf、redis-6380.conf。。。

然后分别修改这些配置文件中 port、cluster-config-file、pidfile、logfile、dir、dbfilename、appendfilename等属性,使每个配置文件中,上述配置项的内容互不相同

然后分别以这些配置文件启动服务器实例,此时这六个节点互相孤立,未构成集群:

从日志可以看到,此时以集群模式运行:

89:M 06 May 2019 11:12:14.390 * No cluster configuration found, I'm 65f40c78c2fa077483faecfe75a648ffd68a74c1
                _._
           _.-``__ ''-._
      _.-``    `.  `_.  ''-._           Redis 5.0.4 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._
 (    '      ,       .-`  | `,    )     Running in cluster mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 89
  `-._    `-._  `-./  _.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |           http://redis.io
  `-._    `-._`-.__.-'_.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |
  `-._    `-._`-.__.-'_.-'    _.-'
      `-._    `-.__.-'    _.-'
          `-._        _.-'
              `-.__.-'

并且没有找到集群配置文件(于是会自动创建一个),后面的ID是该节点的唯一ID,节点间通过ID互相识别,而非host、port。

需要从客户端发起 cluster meet 命令使节点加入集群,这里让6380、6381分别加入集群,剩下的节点处理方式相同:

127.0.0.1:6380> cluster meet 127.0.0.1 6379
OK
...
127.0.0.1:6381> cluster meet 127.0.0.1 6380
OK

尝试插入数据,却提示集群状态为down:

127.0.0.1:6379> set hello world
(error) CLUSTERDOWN The Cluster is down

使用cluster nodes或cluster info命令查看集群状态:

127.0.0.1:6379> cluster info
cluster_state:fail
cluster_slots_assigned:0
...
cluster_known_nodes:6
...

集群状态为fail,从下一行可以看出来,原因是槽还没有进行分配。此外也还没有设置副本。

redis-cli提供了一个快捷命令(需要先使用cluster forget命令将6379、6380、6381节点恢复到相互独立的状态):

root@Yhc-Surface:~# redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:6383 to 127.0.0.1:6379
Adding replica 127.0.0.1:6384 to 127.0.0.1:6380
Adding replica 127.0.0.1:6382 to 127.0.0.1:6381
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 65f40c78c2fa077483faecfe75a648ffd68a74c1 127.0.0.1:6379
   slots:[0-5460] (5461 slots) master
M: 120bf876d9d30d9e9585f7161ad9d6820295efec 127.0.0.1:6380
   slots:[5461-10922] (5462 slots) master
M: 7715a4d32b681bfb7ba4d0f9033f33c32c5117b2 127.0.0.1:6381
   slots:[10923-16383] (5461 slots) master
S: 636f6eaf5c3f11942a4a6633e9bdfa9446b83686 127.0.0.1:6382
   replicates 120bf876d9d30d9e9585f7161ad9d6820295efec
S: 41503f4809a3fbb16fdac6f008fca2c6c1aaa389 127.0.0.1:6383
   replicates 7715a4d32b681bfb7ba4d0f9033f33c32c5117b2
S: 002c91cd8ea6581ed6ee0a43c677246f2428553d 127.0.0.1:6384
   replicates 65f40c78c2fa077483faecfe75a648ffd68a74c1
Can I set the above configur
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值