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?)
Node構築プロセス:
Nodeの選択方法:
Jedis shardingのデフォルトのコンシステンシhashアルゴリズムはcache-onlyのシナリオに適しており、データの永続化にはあまり適していない.永続的なストレージの場合、hashアルゴリズム(後述のInnerHashingに参加する)を書き換える必要がある「強いhash」スライスを使用することができます.強いhashアルゴリズムでは、仮想ノードが存在する物理ノードが故障すると、データにアクセスできなくなります.すなわち、失効したserverを仮想ノードリストから削除できません.
二、API
ShardedJedis
ShardedJedisPool & ShardedJedisPipeline
Something:
キャッシュ・テクノロジーにより、システムのパフォーマンスが向上するだけでなく、システムの障害も緩和されます.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