Redis集群

随着大型网站数据量和对系统可用性要求的提升,单机版的Redis越来越难以满足需要,因此我们需要使用Redis集群来提供服务。

目前主流的Redis集群解决方案有三类,它们都是通过将key分散到不同的redis实例上来提高整体能力, 这种方法称为分片(sharding):

  • 服务端分片: 客户端与集群中任意的节点通信,服务端计算key在哪一个节点上,若不再当前节点上则通知客户端应访问的节点。 典型代表为官方推出的Redis Cluster
  • 客户端分片: 客户端计算key应在集群中的哪一个节点上,并与该节点通信。典型代表为ShardedJedis
  • 代理分片: 客户端与集群中的代理(proxy)通信,代理与节点通信进行操作。典型代表为Codis

单机版的Redis中单条指令的执行总是原子性的,在集群中则难以保证这一性质,某些指令可能无法在集群中使用或者受到限制。

若需要使用这些指令或需要它们保持原子性,可以采用单机版Redis和集群搭配使用的方法。将主要业务部署在集群上,将需要较多支持的服务部署在单机版Redis上。

三种集群实现方式各有有缺,本文分别进行简单介绍它们的特性。本文不会详细介绍三种方式的使用方式,只对其架构和特性进行对比,帮助读者选择合适的解决方案。

基本概念

哈希槽

哈希槽(hash slot)是来自Redis Cluster的概念, 但在各种集群方案都有使用。

哈希槽是一个key的集合,Redis集群共有16384个哈希槽,每个key通过CRC16散列然后对16384进行取模来决定该key应当被放到哪个槽中,集群中的每个节点负责一部分哈希槽。

以有三个节点的集群为例:

  • 节点A包含0到5500号哈希槽
  • 节点B包含5501到11000号哈希槽
  • 节点C包含11001到16384号哈希槽

这样的设计有利于对集群进行横向伸缩,若要添加或移除节点只需要将该节点上的槽转移到其它节点即可。

在某些集群方案中,涉及多个key的操作会被限制在一个slot中,如Redis Cluster中的mget/mset操作。

HashTag

HashTag机制可以影响key被分配到的slot,从而可以使用那些被限制在slot中操作。

HashTag即是用{}包裹key的一个子串,如{user:}1, {user:}2

在设置了HashTag的情况下,集群会根据HashTag决定key分配到的slot, 两个key拥有相同的HashTag:{user:}, 它们会被分配到同一个slot,允许我们使用MGET命令。

通常情况下,HashTag不支持嵌套,即将第一个{和第一个}中间的内容作为HashTag。若花括号中不包含任何内容则会对整个key进行散列,如{}user:

HashTag可能会使过多的key分配到同一个slot中,造成数据倾斜影响系统的吞吐量,务必谨慎使用。

主从模型

几种流行的Redis集群解决方案都没有将一个key写到多个节点中,若某个节点故障则无法访问访问其上的key这显然是不满足集群的分区容错性的。

Redis集群使用主从模型(master-slave)来提高可靠性。每个master节点上绑定若干个slave节点,当master节点故障时集群会推举它的某个slave节点代替master节点。

体验Redis集群

Redis Cluster

为了便于部署本文使用docker部署了一个简单集群:

docker run -i -t -p 7000:7000 -p 7001:7001 -p 7002:7002 -p 7003:7003 -p 7004:7004 -p 7005:7005 -p 7006:7006 -p 7007:7007 grokzen/redis-cluster

打开另一个终端窗口, 启动redis客户端:

redis-cli -c -p 7000

在redis客户端中尝试进行操作:

127.0.0.1:7000> set a 1
-> Redirected to slot [15495] located at 127.0.0.1:7002
OK
127.0.0.1:7002> get a
"1"
127.0.0.1:7002> mset ab 2 ac 3
(error) CROSSSLOT Keys in request don't hash to the same slot
127.0.0.1:7002> mset {a}b 2 {a}c 3
OK

上述示例中, 执行set a命令时客户端被重定向到了其它节点。

mset ab 2 ac 3命令因为key被分配到不同的slot中导致CROSSSLOT错误,而使用HashTag机制mset {a}b 2 {a}c 3就可以解决这个问题。

更多的内容可以参考Redis Cluster中文文档

ShardedJedis

Jedis是一个流行的Java语言Redis客户端,在Redis官方提供Redis Cluster之前便实现了客户端集群功能。

ShardedJedis使用一致性哈希算法进行数据分片,不支持涉及多个key的命令, 其不支持的命令可以参考MultiKeyCommands

JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(2);
poolConfig.setMaxIdle(1);
poolConfig.setMaxWaitMillis(2000);

final String HOST = "127.0.0.1";
JedisShardInfo shardInfo1 = new JedisShardInfo(HOST, 6379);
JedisShardInfo shardInfo2 = new JedisShardInfo(HOST, 6380);
JedisShardInfo shardInfo3 = new JedisShardInfo(HOST, 6381);

ShardedJedisPool jedisPool = new ShardedJedisPool(poolConfig, Arrays.asList(shardInfo1, shardInfo2, shardInfo3));

try(ShardedJedis jedis = jedisPool.getResource()) {
    jedis.set("a", "1");
    jedis.set("b", "2");
    System.out.println(jedis.get("a"));
}

在初始化ShardedJedisPool时设置keyTagPattern,匹配keyTagPattern的key会被分配到同一个实例中。

Codis

Codis是豌豆荚开源的代理式Redis集群解决方案,因为Twemproxy缺乏对弹性伸缩的支持,很多企业选择了经过生产环境检验的Codis。

Codis的安装和使用方法可以参考官方文档, 为了方便起见我们使用ReleaseBinary文件安装Codis-Server和Codis-Proxy。

或者使用第三方开发者制作的Docker镜像:

docker run -d --name="codis" -h "codis" -p 18087:18087 -p 11000:11000 -p 19000:19000 ruo91/codis
docker exec codis /bin/bash codis-start all start

使用redis客户端连接19000端口,尝试进行操作:

127.0.0.1:19000> set a  1
OK
127.0.0.1:19000> get a
"1"
127.0.0.1:19000> mset ab 2 ac 3
OK
127.0.0.1:19000> mset {a}b 2 {a}c 3
OK

Codis也支持HashTag, 不过Codis已经解决了大多数命令的slot限制。

集群方案对比

协议支持

命令 Redis Cluster ShardedJedis Codis
mget/mset 仅限同一个slot 不支持 失去原子性
keys 仅限同一个slot 不支持 不支持
scan 仅限同一个slot 不支持 仅限同一个slot(SLOTSSCAN命令)
rename 仅限同一个slot 不支持 不支持
pipeline 不支持 不支持 支持
事务 支持相同slot 不支持 不支持
发布/订阅 支持 不支持 不支持
eval 仅限同一slot 不支持 支持
posted @ 2018-03-18 15:04  -Finley-  阅读(6818)  评论(0编辑  收藏  举报