分片连接池
因为每次创建和销毁Jedis的连接非常耗费资源,所以 引入了分片连接池JedisPool,但是JedisPool里只能 存放一个Jedis节点的连接,所以JedisSharded封装 了分片对象,以及ShardedJedisPool.
自己实现的分片对象的封装
代码语言:javascript复制/*
* 自定义封装底层计算分片对象
*/
public class HashJedisPool {
private List<JedisPool> nodes = new ArrayList();
int n;
public HashJedisPool(List<String> nodes) {
this.n = nodes.size();
for (String node : nodes) {
String host = node.split(":")[];
int port = Integer.parseUnsignedInt(node.split(":")[]);
JedisPool jedisPool = new JedisPool(host,port);
this.nodes.add(jedisPool);
}
}
public void set(String key, String value) {
int i = (key.hashCode()&Integer.MAX_VALUE)%n;
JedisPool jedisPool = nodes.get(i);
Jedis jedis = jedisPool.getResource();
jedis.set(key,value);
jedisPool.returnResource(jedis);
}
public String get(String key) {
int i = (key.hashCode()&Integer.MAX_VALUE)%n;
JedisPool jedisPool = nodes.get(i);
System.out.println(i);
Jedis jedis = jedisPool.getResource();
String value = jedis.get(key);
jedisPool.returnResource(jedis);
return value;
}
}
Jedis客户端封装的分片对象
代码语言:javascript复制/*
* 使用Jedis底层封装好的分片对象
*/
@Test
public void test04() {
List<JedisShardInfo> infos = new ArrayList<JedisShardInfo>();
infos.add(new JedisShardInfo("10.42.167.114",));
infos.add(new JedisShardInfo("10.42.167.114",));
infos.add(new JedisShardInfo("10.42.167.114",));
ShardedJedis jedis = new ShardedJedis(infos);
jedis.set("name","piaolaoshi");
System.out.println(jedis.get("name"));
}
Jedis客户端的分片连接池
代码语言:javascript复制/*
* 分片对象连接池
*/
@Test
public void test05() {
List<JedisShardInfo> infos = new ArrayList<JedisShardInfo>();
infos.add(new JedisShardInfo("10.42.167.114",));
infos.add(new JedisShardInfo("10.42.167.114",));
infos.add(new JedisShardInfo("10.42.167.114",));
ShardedJedis jedis = new ShardedJedis(infos);
JedisPoolConfig config = new JedisPoolConfig();
// 最大连接数200
config.setMaxTotal();
// 最大空闲数8
config.setMaxIdle();
// 最下空闲数3
config.setMinIdle();
// 创建分片对象连接池
ShardedJedisPool shardedJedisPool = new ShardedJedisPool(config, infos);
ShardedJedis shardedJedis = shardedJedisPool.getResource();
shardedJedis.set("name","jiangmingyang");
System.out.println(shardedJedis.get("name"));
}
hash取模与hash一致性算法的对比
代码语言:javascript复制根据代码可以看出来我再手动封装分片对象时为了满足
相同key值读取的一致性时使用了hash取模算法
(key.hashcode()&Integer.MAX_VALUE)% 分片个数
但是当我们对分片进行扩容或缩容时,扩容的节点越多
缓存的命中率就会越低,很容易引起缓存的雪崩。所以
Jedis底层在对分片对象进行封装时,使用的是hash
一致性算法。
hash一致性算法
代码语言:javascript复制一致性哈希算法将整个哈希值空间映射成一个虚拟的圆环,
整个哈希空间的取值范围为~-。
整个空间按顺时针方向组织。~-在零点中方向重合。
接下来使用如下算法对服务请求进行映射,将服务请求使用哈
希算法算出对应的hash值,然后根据hash值的位置沿圆环顺时针
查找,第一台遇到的服务器就是所对应的处理请求服务器.
当增加一台新的服务器,受影响的数据仅仅是新添加
的服务器到其环空间中前一台的服务器(也就是顺着逆
时针方向遇到的第一台服务器)之间的数据,其他都不
会受到影响。综上所述,一致性哈希算法对于节点的增
减都只需重定位环空间中的一小部分数据,具有较好的
容错性和可扩展性 [] 。
简单的来说任何一个对象经过hash一致性算法 计算之后都能被映射在一个0~43亿之间的hash环上,分布在不同区间的key值按照顺时针的方向去匹配最近的redis分片对象。这样在reidis节点进行扩容或缩容时受影响的只是 新节点后面一小段区间的部分,随着节点数量 的增多,受影响的区间会越来越小,这也就意味着缓存的命中率会随这节点的增多而增大,这与hash取模算法刚好相反,所以进行分片对象的封装时,Jedis客户端采用了Hash一致性算法。