Jedis整合性hashとsharding


一、Jedis一致性hash
キャッシュ・テクノロジーにより、システムのパフォーマンスが向上するだけでなく、システムの障害も緩和されます.redis 3.0以下のバージョンでは、redis-serverにはshardingの機能はなく、master-slaveモードのみです.現在、企業で使用されているのは、masterでもslaveでもプログラム構成(またはコード)を調整する必要があるm/sモードのみのredisマルチインスタンス配置です.Jedisはプログラミングレベルのsharding方式を提供し,本稿では主に関連APIの使用方法を紹介する.
 
Jedisではshardingはコンシステンシhashアルゴリズムに基づいており、hash値計算はMD 5を補助として採用されている.このアルゴリズムは事実上の基準となっているようだが、新しいバージョンではグーグルのmurmur_が採用されている.hashアルゴリズム(MD 5 is really not good?)
 
public interface Hashing {
  public static final Hashing MURMUR_HASH = new MurmurHash();
  public ThreadLocal<MessageDigest> md5Holder = new ThreadLocal<MessageDigest>();
         //   MD5    hash    
  public static final Hashing MD5 = new Hashing() {
    public long hash(String key) {
      return hash(SafeEncoder.encode(key));
    }

    public long hash(byte[] key) {
      try {
        if (md5Holder.get() == null) {
          md5Holder.set(MessageDigest.getInstance("MD5"));
        }
      } catch (NoSuchAlgorithmException e) {
        throw new IllegalStateException("++++ no md5 algorythm found");
      }
      MessageDigest md5 = md5Holder.get();

      md5.reset();
      md5.update(key);
      byte[] bKey = md5.digest(); //   MD5    
      //            ,      32 int .  
      //       ,    key hash   “  ”/“  ”  
      //   hash     ,      hash   (    “    ”   ) 
      long res = ((long) (bKey[3] & 0xFF) << 24) | ((long) (bKey[2] & 0xFF) << 16)
          | ((long) (bKey[1] & 0xFF) << 8) | (long) (bKey[0] & 0xFF);
      return res;
    }
  };

  public long hash(String key);

  public long hash(byte[] key);
}

 
Node構築プロセス:
//shards           redis-server    ,  :ip,port,weight,name
//  weight   ,     “    ” “  ”(  ),    ,     hash       
//--         。
//  name “    ”,jedis  name  “  hash ”       。
//---
//   hash  ,    “    ”    “hash ”,     server     “    ”(API  )
//         = “      ” * weight,  server “    ”   “hash”           
//       2^32.  “    ” hash            。
//   :0-->vnode1(:1230)-->vnode2(:2800)-->vnode3(400000)---2^32-->0
//   “    ”    ”  hash“    (  /    ),      “    ”    hash  ,
//     ,     (     ,      )“    ”      hash   。
//  hash  “2000”      vnode1   。
//---
private void initialize(List<S> shards) {
	nodes = new TreeMap<Long, S>();//    ,  TreeMap  :  ,   

	for (int i = 0; i != shards.size(); ++i) {
	    final S shardInfo = shards.get(i);
	    if (shardInfo.getName() == null)
                //     “name” , “SHARD-NODE”  “    ”hash      
                //"      " 160,    ??
                //    server “    ”      ,       。
	    	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);
	    	}
	    resources.put(shardInfo, shardInfo.createResource());
	}
}

 
Nodeの選択方法:
public R getShard(String key) {
	return resources.get(getShardInfo(key));
}
//here:
public S getShardInfo(byte[] key) {
        //  >=key “    ”   
	SortedMap<Long, S> tail = nodes.tailMap(algo.hash(key));
        //     “    ”,       。
	if (tail.size() == 0) {
	    return nodes.get(nodes.firstKey());
	}
        //    ,     (>=key)   “    ”      
	return tail.get(tail.firstKey());
}

 
Jedis shardingのデフォルトのコンシステンシhashアルゴリズムはcache-onlyのシナリオに適しており、データの永続化にはあまり適していない.永続的なストレージの場合、hashアルゴリズム(後述のInnerHashingに参加する)を書き換える必要がある「強いhash」スライスを使用することができます.強いhashアルゴリズムでは、仮想ノードが存在する物理ノードが故障すると、データにアクセスできなくなります.すなわち、失効したserverを仮想ノードリストから削除できません.
 
二、API
    ShardedJedis
        JedisShardInfo sd1 = new JedisShardInfo("127.0.0.1", 6379, 15000);
        sd1.setPassword("123456");
        JedisShardInfo sd2 = new JedisShardInfo("127.0.0.1", 6479, 15000);
        sd2.setPassword("123456");
        List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
        shards.add(sd1);
        shards.add(sd2);
        ShardedJedis shardedJedis = new ShardedJedis(shards, new InnerHashing());
        String key = "k2sdjowejjroer3";
        shardedJedis.set(key, "v2");
        Charset charset = Charset.forName("utf-8");
        //      key      ,    Innerhashing.hash(String)    
        System.out.println(shardedJedis.get("k2").getBytes(charset));

// Jedis    hash        ,         
public class InnerHashing implements Hashing {
    static Charset charset = Charset.forName("utf-8");

    @Override
    public long hash(String key) {
        return hash(key.getBytes(charset));
    }

    @Override
    public long hash(byte[] key) {
        int hashcode = new HashCodeBuilder().append(key).toHashCode();
        return hashcode & 0x7FFFFFFF;
    }
}

 
<bean id="shardedJedis" class="redis.clients.jedis.ShardedJedis">
	<constructor-arg>
		<list>
			<bean class="redis.clients.jedis.JedisShardInfo">
				<constructor-arg value="127.0.0.1"></constructor-arg>
				<constructor-arg value="6379"></constructor-arg>
				<property name="password" value="0123456"></property>
			</bean>
			<bean class="redis.clients.jedis.JedisShardInfo">
				<constructor-arg value="127.0.0.1"></constructor-arg>
				<constructor-arg value="6379"></constructor-arg>
				<property name="password" value="0123456"></property>
			</bean>
		</list>
	</constructor-arg>
</bean>

 
     ShardedJedisPool & ShardedJedisPipeline
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(32);
        config.setMaxIdle(6);
        config.setMinIdle(0);
        config.setMaxWaitMillis(15000);

        JedisShardInfo sd1 = new JedisShardInfo("127.0.0.1", 6379, 15000);
        sd1.setPassword("123456");
        JedisShardInfo sd2 = new JedisShardInfo("127.0.0.1", 6479, 15000);
        sd2.setPassword("123456");
        List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
        shards.add(sd1);
        shards.add(sd2);

        ShardedJedisPool sjp = new ShardedJedisPool(config, shards);
        ShardedJedis shardedJedis = sjp.getResource();
        try {
            System.out.println(shardedJedis.get("k2"));

            ShardedJedisPipeline pipeline = new ShardedJedisPipeline();
            pipeline.setShardedJedis(shardedJedis);
            pipeline.set("k4", "v4");
            pipeline.set("k5", "v5");
            pipeline.get("k5");
            List<Object> all = pipeline.syncAndReturnAll();
            for (Object e : all) {
                System.out.println(e);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sjp.returnResource(shardedJedis);
        }

 
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
	<property name="maxActive" value="32"></property>
	<property name="maxIdle" value="6"></property>
	<property name="maxWait" value="15000"></property>
	<property name="minEvictableIdleTimeMillis" value="300000"></property>
	<property name="numTestsPerEvictionRun" value="3"></property>
	<property name="timeBetweenEvictionRunsMillis" value="60000"></property>
	<property name="whenExhaustedAction" value="1"></property>
</bean>
<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool" destroy-method="destroy">
	<constructor-arg ref="jedisPoolConfig"></constructor-arg>
	<constructor-arg>
		<list>
			<bean class="redis.clients.jedis.JedisShardInfo">
				<constructor-arg value="127.0.0.1"></constructor-arg>
				<constructor-arg value="6379"></constructor-arg>
				<property name="password" value="0123456"></property>
			</bean>
			<bean class="redis.clients.jedis.JedisShardInfo">
				<constructor-arg value="127.0.0.1"></constructor-arg>
				<constructor-arg value="6379"></constructor-arg>
				<property name="password" value="0123456"></property>
			</bean>
		</list>
	</constructor-arg>
</bean>

    
    Something:
redis         IO server,            (server )      rebalance,    ,        .

           :
1)   web portal  ,   redis       ,  web       IP       (      redis       ,    ).
2)         redis"  /  ",  redis     zookeeper (  configserver);
3)   redis-client ,   configserver,     redis  ;    redis-client.
4) redis-client           configserver    ,     redis     .
5)   redis    ,   redis-client     ,     "   hash ".
6)   "   hash "   ,             ,   hash         .

     ,         (db,  JMS  )   redis           ..  "      hash "   ,       ,           join  remove,            ..

 
 Jedis   hash sharding ,     master slave