Redis数据分片

Redis的分片(Sharding或者Partitioning)技术是指将数据分散到多个Redis实例中的方法,分片之后,每个redis拥有一部分原数据集的子集。在数据量非常大时,这种技术能够将数据量分散到若干主机的redis实例上,进而减轻单台redis实例的压力。分片技术能够以更易扩展的方式使用多台计算机的存储能力(这里主要指内存的存储能力)和计算能力:

(1)从存储能力的角度,分片技术通过使用多台计算机的内存来承担更大量的数据,如果没有分片技术,那么redis的存储能力将受限于单台主机的内存大小。

(2) 从计算能力的角度,分片技术通过将计算任务分散到多核或者多台主机中,能够充分利用多核、多台主机的计算能力。


下面将以举例的方式说明分片技术及其存在的优势:

示例1:未采用分片技术,有1000万条用户信息数据,以键值对:UsrID:UsrInfo的形式存储在一个redis实例中,此时所有的用户信息都会存储在一个redis实例中,对这1000万条数据的所有插、查、删、该操作压力都会集中在这个redis所在的主机上;此时所要考虑的问题不仅有存储和操作对该主机的压力,还有该主机失效时将导致所有操作都无法进行的问题。

示例2:采用分片技术;有1000万条用户信息数据,以键值对:UsrID:UsrInfo的形式存储于redis中,此时有4台主机,每台主机运行一个Redis实例:主机A (Redis1)、主机B(Redis2)、主机C(Redis3)、主机D(Redis4),分片时算法为:

redis_index = 用户的ID % 4 + 1;

例如ID为10000654则可得到到redis_index的值:10000654 % 4 + 1 = 1,即用户10000654的信息将被放到Redis1上,所有对用户1000654的操作也将被分片到Redis1上;假如用户ID以顺序方式出现,这1000万条用户信息将被平均分配到这四台主机的各Redis实例上。

采用分片算法
 

采用分片之后,数据将被分散到4个redis实例中,对数据的操作也被分散到每个redis实例中,此时单台主机的压力将大大减轻。

分片的部署,即实例2中分片算法被放在哪里?是在分片时需要首先考虑的问题,分片部署方式一般分为以下三种:

(1)在客户端做分片;这种方式在客户端确定要连接的redis实例,然后直接访问相应的redis实例;

(2)在代理中做分片;这种方式中,客户端并不直接访问redis实例,它也不知道自己要访问的具体是哪个redis实例,而是由代理转发请求和结果;其工作过程为:客户端先将请求发送给代理,代理通过分片算法确定要访问的是哪个redis实例,然后将请求发送给相应的redis实例,redis实例将结果返回给代理,代理最后将结果返回给客户端。

(3)在redis服务器端做分片;这种方式被称为“查询路由”,在这种方式中客户端随机选择一个redis实例发送请求,如果所请求的内容不再当前redis实例中它会负责将请求转交给正确的redis实例,也有的实现中,redis实例不会转发请求,而是将正确redis的信息发给客户端,由客户端再去向正确的redis实例发送请求。

上面主要描述了分片的优点,当然分片的存在也有缺陷,例如:

(1) 通常无法支持涉及多键的操作;在redis中有很多一次操作多个key的操作,例如求集合交集的SINTER操作,该操作将涉及到多个键,而这多个键有可能被分片到不同的redis实例中,此时就无法执行这种操作。

(2)Redis的事务操作中涉及多个键时也不能用;

(3)分片将导致数据处理更加复杂;例如在分片过程中,随着redis实例的增加,数据备份等操作都将会变得更加复杂。

(4)Redis目前不支持动态分片操作,扩容和缩容操作都会比较复杂,尤其分片操作部署在客户端时,需要重新配置和启动客户端。在使用过程中缩容用的不多,扩容可以采用后面介绍的预分片策略来缓解此问题。

预分片技术
 

因为使用了一致性哈稀进行分片,那么不同的key分布到不同的Redis-Server上,当我们需要扩容时,需要增加机器到分片列表中,这时候会使得同样的key算出来落到跟原来不同的机器上,这样如果要取某一个值,会出现取不到的情况。而且在正常运营环境中,一般所存储的数据会逐渐增加,可能今天只要10个redis实例就能应付,但是到了一年以后就需要50个redis实例才能支撑,因此,redis的扩容是经常用到的功能,在redis的分布式部署中,有预分片技术是非常好用的方法之一;


预分片技术是指在开始时就启动足够多的redis实例(例如32或64个,估计一下够以后扩展用就行了),等到后续需要扩容的时候,只需要将其中一部分的redis实例转移到新增加的机子上即可,在redis实例迁移过程中使用redis的复制功能可以最大限度的降低redis的停工时间甚至可以做到没有停工时间。由于redis实例是轻量级的进程,而且占用内存较少,这里指单纯的空的redis实例,一个空的redis实例大约占用1M的内存;因此,这种方式即不会占用太多系统开销,又便于实现;

Redis的预分片技术可以按照以下步骤进行实例迁移操作:

(1)在新机子上启动新的redis实例;

(2)将新redis实例作为slave将原redis实例作为master,将数据从原redis实例迁移到新redis实例上;

(3)停止客户端(分片操作在客户端上时)或代理服务器(分片操作在代理上)

(4)更新客户端或者代理服务器中的配置信息,去掉被迁移的原redis实例的ip和端口等信息,加上新启动redis实例的IP地址和端口;

(5)向新启动的redis发送SLAVEOF NOONE命令,终止新redis实例对原redis实例的从属关系;

(6)重启客户端程序或者代理程序,此时它们将会使用新的redis实例;
 

(7)关掉被迁移走数据的原redis实例;

 

Jedis分片连接池

Jedis通过一致性哈希分片算法来实现数据分片,一致性哈希算法:http://blog.csdn.net/cywosp/article/details/23397179/。简单用Jedis分片连接池来实现多Redis的master节点进行数据分片,步骤如下:

1. 部署2个Redis master节点。

2. 用ShardedJedisPool连接Redis maser节点,并将数据根据一致性哈希算法存放到不同的master节点,从而达成数据分片。

客户端代码如下:

public class RedisShardPoolTest {
    static ShardedJedisPool pool;
    static{
        JedisPoolConfig config =new JedisPoolConfig();//Jedis池配置
        config.setMaxActive(500);//最大活动的对象个数
          config.setMaxIdle(1000 * 60);//对象最大空闲时间
          config.setMaxWait(1000 * 10);//获取对象时最大等待时间
          config.setTestOnBorrow(true);
        String hostA = "10.7.12.52";
          int portA = 6379;
          String hostB = "10.7.112.52";
          int portB = 6379;
        List<JedisShardInfo> jdsInfoList =new ArrayList<JedisShardInfo>(2);
        JedisShardInfo infoA = new JedisShardInfo(hostA, portA);
        infoA.setPassword("redis.360buy");
        JedisShardInfo infoB = new JedisShardInfo(hostB, portB);
        infoB.setPassword("redis.360buy");
        jdsInfoList.add(infoA);
        jdsInfoList.add(infoB);
       
        pool =new ShardedJedisPool(config, jdsInfoList, Hashing.MURMUR_HASH,
Sharded.DEFAULT_KEY_TAG_PATTERN);
    }
   
    /**
     * @param args
     */
    public static void main(String[] args) {
        for(int i=0; i<100; i++){
           String key =generateKey();
           //key += "{aaa}";
           ShardedJedis jds =null;
           try {
               jds =pool.getResource();
               System.out.println(key+":"+jds.getShard(key).getClient().getHost());
               System.out.println(jds.set(key,"1111111111111111111111111111111"));
           }catch (Exception e) {
               e.printStackTrace();
           }
           finally{
               pool.returnResource(jds);
           }
        }
    }
 
    private static int index = 1;
    public static String generateKey(){
        return String.valueOf(Thread.currentThread().getId())+"_"+(index++);
    }
}

通过执行结果会发现这100条数据会被分开存放到10.7.12.52和10.7.112.52机器上的master节点中,查看源码会发现jds.set(key,value)方法会调用jds.getShared(key)来根据key的hash值来获取该key应该存放到那个节点上。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值