• 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 不支持 支持
  • 相关阅读:
    NS3笔录
    网络性能指标的几个定义
    获取Emum类型值的数量
    Container类型元素累加
    ax用代码调用静态查询
    FormRun类的方法detach()作用
    Num2Str函数使用介绍
    查询生产单PO的位置
    AX使用临时表作为数据源
    Date2Str函数使用介绍
  • 原文地址:https://www.cnblogs.com/Finley/p/8595506.html
Copyright © 2020-2023  润新知