`
ssxxjjii
  • 浏览: 950401 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Jedis 默认sharding实现原理分析

 
阅读更多

当信息量较大时,我们就需要将信息保存在多台机器上。如何均匀分配数据呢?
redis.clients.jedis.ShardedJedisPool.java 为我们提供了一个简单一样的数据分箱的实现,下面分析一下其原理。

从构造方法入手:

  1. public ShardedJedisPool(final GenericObjectPool.Config poolConfig,
  2.             List<JedisShardInfo> shards, Hashing algo, Pattern keyTagPattern) {
  3.      super(poolConfig, new ShardedJedisFactory(shards, algo, keyTagPattern));
  4. }

poolConfig:同JedisPool的设置,参见《常见JedisConnectionException异常分析》http://blog.csdn.net/fachang/article/details/7984123
ShardedJedisFactory:继承自org.apache.commons.pool.BasePoolableObjectFactory<T>.java类,提供了为连接池创建连接实例的抽象实现,api如下:

 api地址:http://commons.apache.org/pool/apidocs/index.html?org/apache/commons/pool/impl/GenericObjectPool.html

  1.  voidactivateObject(T obj)    No-op.
  2.  voiddestroyObject(T obj)    No-op.
  3.  abstract  T makeObject()     Creates an instance that can be served by the pool.
  4.  voidpassivateObject(T obj)     No-op.
  5.  booleanvalidateObject(T obj)     This implementation always returns true.

          
 其中我们需要关心的是ShardedJedisFactory对makeObject()抽象方法的实现,源码如下:

  1.  public Object makeObject() throws Exception {
  2.  ShardedJedis jedis = new ShardedJedis(shards, algo, keyTagPattern);
  3.     return jedis;
  4.  }

 接着探寻redis.clients.jedis.ShardedJedis extends BinaryShardedJedis类的构造方法:

  1.   public ShardedJedis(List<JedisShardInfo> shards, Hashing algo, Pattern keyTagPattern) {
  2. super(shards, algo, keyTagPattern);
  3.   }

 调用了父类redis.clients.jedis.BinaryShardedJedis extends Sharded<Jedis, JedisShardInfo>的构造方法,源码如下:

  1.  public BinaryShardedJedis(List<JedisShardInfo> shards, Hashing algo, Pattern keyTagPattern) {
  2. super(shards, algo, keyTagPattern);
  3.  }

 

 接着看其父类redis.clients.util.Sharded<R, S extends ShardInfo<R>>的构造方法:

 连接池存储信息:
 存储连接池pool中各个连接信息JedisShardedInfo,实际操作中一个JedisShardedInfo根据其名字,对应160个沙箱孔(key),这样沙箱孔越多,数据将分布越均匀。
 private TreeMap<Long, S> nodes; 
 存储连接信息JedisShardedInfo到实际连接实例ShardedJedis的映射。

  1.  private final Map<ShardInfo<R>, R> resources = new LinkedHashMap<ShardInfo<R>, R>(); 
  2.  public Sharded(List<S> shards, Hashing algo, Pattern tagPattern) {
  3.         this.algo = algo;
  4.         this.tagPattern = tagPattern;
  5.         initialize(shards);
  6.  }

 真正的分校操作应该就是initialize(List<S> shards)方法了,源码如下:

  1.   private void initialize(List<S> shards) {
  2.       nodes = new TreeMap<Long, S>();
  3.       for (int i = 0; i != shards.size(); ++i) {
  4.           final S shardInfo = shards.get(i);
  5.           if (shardInfo.getName() == null)
  6.           for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
  7.           nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n), shardInfo);
  8.           }
  9.           else
  10.           for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
  11.           nodes.put(this.algo.hash(shardInfo.getName() + "*" + shardInfo.getWeight() + n), shardInfo);
  12.           }
  13.           resources.put(shardInfo, shardInfo.createResource());
  14.       }
  15.   }

 其中我们看出JedisShardedPool通过每一个JedisShardedInfo配置的连接的name属性类分箱。具体做法是,每个Redis连接根据其名字+权重+计数号等信息

进行160次哈希计算作为160个 “沙箱孔”key(一个Long值)都对应一个redis连接信息JedisShardedInfo实例。假设有2个redis连接信息如下:

  1.  List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
  2.  shards.add(new JedisShardInfo("localhost", 6379, "master1"));
  3.  shards.add(new JedisShardInfo("localhost", 6380, "master2"));

 
 则:
 名字为master1,端口为6379的连接在pool中对应根据名字master1+权重+计数号生成的160个沙箱孔。
 名字为master2,端口为6380的连接在pool中对应根据名字master2+权重+计数号生成的160个沙箱孔.
 注:为什么会生成160个呢?个人认为这个数字是考虑到redis key 值能在各连接中均匀分布而为之。
 

 这样当我们通过key对redis进行操作时,会用同样的hash算法对该key进行hash操作,然后在所有的沙箱孔(连接的keys)中找到那个力该key值最接近的那个孔(连接在pool中的key值),

获取 redis连接,进行相应的操作。

 
 举个例子,进行如下操作:

  1.  ShardedJedis jedis = shardedJedisPool.getResource();
  2.  jedis.set("key1", "value1");

 
 看ShardedJedis.java源码如下:

  1.   public String set(String key, String value) {
  2. Jedis j = getShard(key);
  3. return j.set(key, value);
  4.   }

  
 首先要调用getShard(key)方法获取当前要操作的key对应的连接实例,继续看getShard()方法源码:

  1.   public R getShard(String key) {
  2.     return resources.get(getShardInfo(key));
  3.   }

  
 为从resources中获取连接实例,需要知道该连接实例在reeMap<Long, S> nodes 中对应哪个沙箱孔?

  1.  public S getShardInfo(byte[] key) {
  2.     SortedMap<Long, S> tail =nodes.tailMap(algo.hash(key));
  3.     if (tail.isEmpty()) {
  4.         return nodes.get(nodes.firstKey());
  5.     }
  6.     return tail.get(tail.firstKey());
  7.  }

 

  1.  public S getShardInfo(String key) {
  2.     return getShardInfo(SafeEncoder.encode(getKeyTag(key)));
  3.  }

 
 重点在getShardInfo(byte[] key)的实现,根据key的hash值,找到nodes中比它大的key值(沙箱孔)。如果没有,则拿第一个孔,如果存在则取比它大的第一个孔。
 这样对key的save/query类操作都能映射到沙箱中的同一个孔,获取相同的连接,保证数据的一致和完整性。
 
 以上是Jedis沙盒机制的默认实现,但在实际应用中我们不一定要采用这样的机制。

分享到:
评论

相关推荐

    redis实现分布式锁(java/jedis)

    redis实现分布式锁(java/jedis),其中包含工具方法以及使用demo 本资源是利用java的jedis实现 redis实现分布式锁(java/jedis),其中包含工具方法以及使用demo 本资源是利用java的jedis实现

    java redis使用之利用jedis实现redis消息队列.docx

    ### Java Redis 使用之利用 Jedis 实现 Redis 消息队列 #### 一、引言 随着互联网应用的发展,消息队列在系统架构中的地位愈发重要。它不仅可以提高系统的响应速度,还可以帮助开发者构建出更加健壮、可扩展的应用...

    jedis-2.5.1.jar

    Jedis 是 Redis 官方首选的 Java 客户端开发包。 实例方法: ? 1 import redis.clients.jedis.* ? 1 2 3 Jedis jedis = new Jedis("localhost"); jedis.set("foo", "bar"); String value = jedis.get("foo"); 支持...

    jedis jedis.jar

    使用Jedis,开发者可以轻松实现以下功能: 1. 数据缓存:利用Redis的高速读写能力,存储和检索频繁访问的数据,提升应用性能。 2. 分布式锁:通过设置和删除特定键来实现分布式锁,确保在多线程或多节点环境下对...

    jedis-3.0.0源码

    通过对Jedis 3.0.0源码的深入分析,我们可以看到其在连接管理、命令执行、集群支持、管道模式、数据类型操作和异常处理等方面的设计和实现。这些特性使得Jedis成为了Java开发者与Redis交互的首选客户端。理解并掌握...

    jedis-4.3.1

    要开始使用 Jedis,首先将其作为依赖项添加到您的 Java 项目中。如果使用 Maven: ...Jedis实例实现大多数 Redis 命令。 使用连接池的更简单方法: JedisPooled jedis = new JedisPooled("localhost", 6379);

    jedis相关jar包

    2. **jedis-2.7.0.jar**:这是Jedis的主要库文件,包含了Jedis的所有API和实现。2.7.0是Jedis的一个特定版本,可能包含了对Redis协议的支持,各种命令的实现,以及一些优化和bug修复。使用这个版本的Jedis,开发者...

    jedis源码 (学习jedis)

    源码揭示了这些功能的实现原理。 8. **pipeline和事务的异步优化**: 使用`Pipeline`可以批量发送命令,减少网络通信次数,提高性能。源码中,`Pipeline`类会收集命令并一次性发送,然后一次性解析所有响应。 9. ...

    jedis依赖jar包

    **Jedis:Redis的Java客户端** Jedis是Java开发者用于操作Redis数据库的首选客户端库。Redis是一个开源的、高性能的键值对存储系统,常被用作数据缓存、消息中间件以及数据库。Jedis提供了丰富的API,允许开发人员...

    jedis5.1.0.jar

    jedis5.1.0.jar

    jedis-2.9.0.jar

    jedis-2.9.0.jar jedis-2.9.0 jar 包,不包含源码,源码下载地址: http://download.csdn.net/download/tan3739/9993938 测试代码: 导入依赖包: commons-lang-2.5.jar commons-pool2-2.4.2.jar jedis-2.9.0 jar ...

    Jedis所需jar包

    在Java环境中与Redis进行交互,我们通常会使用Jedis这个客户端库。Jedis提供了丰富的API,能够帮助开发者轻松完成各种Redis操作,如设置和获取键值、执行事务、订阅/发布消息等。 Jedis的主要功能包括: 1. 基本...

    使用redisson替代jedis

    ### 使用Redisson替代Jedis 在分布式系统中,Redis作为一种高性能的键值存储数据库,被广泛应用于缓存、消息队列、数据同步等场景。在Java开发领域,开发者可以选择多种客户端来与Redis进行交互,其中最常用的是...

    jedis单机版,集群版工具类

    在本文中,我们将深入探讨 Jedis 的单机版和集群版使用,以及如何利用它来实现各种 Redis 操作。 ### 单机版 Jedis 使用 在单机版 Redis 设置中,Jedis 作为客户端直接与单个 Redis 服务器进行通信。以下是一些...

    jedis-jedis-2.7.2

    4. **发布/订阅**:Jedis提供了`subscribe()`和`publish()`方法,用于实现Redis的消息发布与订阅功能,从而构建基于消息的分布式系统。 5. **Pipeline**:为了提高性能,Jedis提供了管道模式,允许一次性发送多条...

    jedis安装包

    Jedis是Java开发的一款高效的Redis客户端库,专为处理Redis数据结构而设计。在本文中,我们将深入探讨Jedis的安装、配置、基本操作以及它在实际应用中的重要性。 一、Jedis简介 Jedis是由Xavier Lachier开发的Java...

    jedis-3.0.0.jar、jedis-3.0.0-javadoc.jar、jedis-3.0.0-sources.jar

    3. **jedis-3.0.0-sources.jar**:这个文件包含了Jedis库的源代码,对于想要深入理解Jedis内部实现或者进行二次开发的开发者来说,这是一个非常宝贵的资源。通过阅读源代码,可以了解Jedis如何封装Redis协议,如何...

    Jedis API中文使用文档.-比较详细

    Jedis API中文使用文档详解 Jedis 是 Redis 官方首选的 Java 客户端开发包,用于操作 Redis 数据库。下面是 Jedis API 的详细使用文档,适合新手和老程序员进行复习。 Jedis 的基本使用 Jedis 提供了多种方式来...

    Jedis源码-供查询redis API源码

    `Jedis`类实现了连接的创建、管理以及命令的发送和接收。`Connection`类中包含了TCP套接字操作和协议解析的相关逻辑。 2. **Commands** - Jedis提供了许多接口,如`StringCommands`、`HashCommands`等,每个接口...

Global site tag (gtag.js) - Google Analytics