ShardedJedis的分片原理


ShardedJedisPool xml配置:

<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">
    <constructor-arg index="0">
        <bean class="redis.clients.jedis.JedisPoolConfig"></bean>
    </constructor-arg>
    <constructor-arg index="1">
        <list>
            <bean class="redis.clients.jedis.JedisShardInfo">
                <constructor-arg name="host" value="192.168.233.8"/>
                <constructor-arg name="port" value="6379"/>
            </bean>
            <bean class="redis.clients.jedis.JedisShardInfo">
                <constructor-arg name="host" value="192.168.233.8"/>
                <constructor-arg name="port" value="6381"/>
            </bean>
            <bean class="redis.clients.jedis.JedisShardInfo">
                <constructor-arg name="host" value="192.168.233.8"/>
                <constructor-arg name="port" value="6382"/>
            </bean>
        </list>
    </constructor-arg>
</bean>

xml配置對應的構造方法:

public class ShardedJedisPool extends Pool<ShardedJedis> {
  public ShardedJedisPool(final GenericObjectPoolConfig poolConfig, List<JedisShardInfo> shards) {
    this(poolConfig, shards, Hashing.MURMUR_HASH);
  }
}

ShardedJedisPool使用示例:

ShardedJedisPool pool = ctx.getBean(ShardedJedisPool.class);
ShardedJedis jedis = pool.getResource();
String zhang = jedis.get("zhang");
jedis.close(); 

ShardedJedisPool.getResource 的調用棧:

代碼分析:

//類 redis.clients.util.Sharded<R, S extends ShardInfo<R>>
private final Map<ShardInfo<R>, R> resources = new LinkedHashMap<ShardInfo<R>, R>();

private void initialize(List<S> shards) {
    nodes = new TreeMap<Long, S>();
  //shards是xml中配置的redis.clients.jedis.JedisShardInfo的list
    for (int i = 0; i != shards.size(); ++i) {
        final S shardInfo = shards.get(i);
     //沒有為JedisShardInfo設置name,所以執行if分支,weight默認為1
     //取 SHARD-0-NODE-0 ~ SHARD-0-NODE-159 哈希值
     //取 SHARD-1-NODE-0 ~ SHARD-1-NODE-159 哈希值
     //取 SHARD-2-NODE-0 ~ SHARD-2-NODE-159 哈希值
     //把(哈希值->JedisShardInfo)鍵值對放入nodes中
        if (shardInfo.getName() == null) for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
            nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n), shardInfo);
        }
        else for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
            nodes.put(this.algo.hash(shardInfo.getName() + "*" + shardInfo.getWeight() + n), shardInfo);
        }
        //添加到map中,鍵為JedisShardInfo,值為Jedis
        resources.put(shardInfo, shardInfo.createResource());
    }
}

JedisShardInfo.createResource:

//省略其他代碼
public class JedisShardInfo extends ShardInfo<Jedis> {
  @Override
  public Jedis createResource() {
    return new Jedis(this);
  }
}

存取一個鍵時,根據鍵獲取一個JedisShardInfo,以get為例:

public S getShardInfo(byte[] key) {
   //nodes是TreeMap,由紅黑樹實現,是按鍵值排好序的map,默認為升序
   //假定 algo.hash(key) 的值為10086,該行代碼是取出鍵大於10086的所有鍵值對  
    SortedMap<Long, S> tail = nodes.tailMap(algo.hash(key));
    //如果不存在,則取第一個鍵值對的JedisShardInfo    
    if (tail.isEmpty()) {
        return nodes.get(nodes.firstKey());
    }
    //如果存在這樣的鍵值對,則返回第一個鍵值對中的JedisShardInfo
    return tail.get(tail.firstKey()); 
}

Sharded類中有2個map,TreeMap<Long, S> nodes和Map<ShardInfo<R>, R> resources,

運行時具體是:TreeMap<Long, JedisShardInfo>和Map<JedisShardInfo, Jedis>。

分析以上代碼可知,首先計算出 "SHARD-i-NODE-n" 的哈希值,預先生成一顆紅黑樹,即填充nodes。

當存取鍵值對時,計算鍵的哈希值,然后從紅黑樹上摘下比該值大的第一個節點上的JedisShardInfo,隨后從resources取出Jedis。

假定有紅黑樹如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM