1. 概述
当业务的数据量非常庞大时,需要考虑将数据存储到多个缓存节点上,如何定位数据应该存储的节点,一般用的是一致性哈希算法。
Jedis在客户端角度实现了一致性哈希算法,对数据进行分片,存储到对应的不同的redis实例中。
Jedis对Sharded的实现主要是在ShardedJedis.java
和ShardedJedisPool.java
中。
本文主要介绍ShardedJedis的实现,ShardedJedisPool是基于apache的common-pool2的对象池实现。
2. 继承关系
3. 构造函数
参数
shards
一个JedisShardInfo的列表,一个JedisShardedInfo类代表一个数据分片的主体algo
数据分片算法keyTagPattern
自定义分片算法所依据的key的形式。例如,可以不针对整个key的字符串做哈希计算,而是类似对thisisa{key}中包含在大括号内的字符串进行哈希计算
JedisShardInfo
源码
public class JedisShardInfo extends ShardInfo<Jedis> { public String toString() { return host + ":" + port + "*" + getWeight(); } private int timeout; private String host; private int port; private String password = null; private String name = null; public String getHost() { return host; } public int getPort() { return port; } public JedisShardInfo(String host) { super(Sharded.DEFAULT_WEIGHT); URI uri = URI.create(host); if (uri.getScheme() != null && uri.getScheme().equals("redis")) { this.host = uri.getHost(); this.port = uri.getPort(); this.password = uri.getUserInfo().split(":", 2)[1]; } else { this.host = host; this.port = Protocol.DEFAULT_PORT; } } public JedisShardInfo(String host, String name) { this(host, Protocol.DEFAULT_PORT, name); } public JedisShardInfo(String host, int port) { this(host, port, 2000); } public JedisShardInfo(String host, int port, String name) { this(host, port, 2000, name); } public JedisShardInfo(String host, int port, int timeout) { this(host, port, timeout, Sharded.DEFAULT_WEIGHT); } public JedisShardInfo(String host, int port, int timeout, String name) { this(host, port, timeout, Sharded.DEFAULT_WEIGHT); this.name = name; } public JedisShardInfo(String host, int port, int timeout, int weight) { super(weight); this.host = host; this.port = port; this.timeout = timeout; } public JedisShardInfo(URI uri) { super(Sharded.DEFAULT_WEIGHT); this.host = uri.getHost(); this.port = uri.getPort(); this.password = uri.getUserInfo().split(":", 2)[1]; } @Override public Jedis createResource() { return new Jedis(this); } }
可见JedisShardInfo包含了一个redis节点主机号,端口号,名称,密码等相关信息
要构造一个ShardedJedis,需提供一个或多个JedisShardInfo
最终构造函数的实现在其父类Sharded
4. 哈希环的初始化
Sharded类维护了
TreeMap
基于红黑树实现,用来存放经过一致性哈希计算后的redis节点,LinkedHashMap
用来保存ShardInfo与Jedis实例的对应关系。
定位的流程如下
先在TreeMap中找到对应key所对应的ShardInfo,然后通过ShardInfo在LinkedHashMap中找到对应的Jedis实例
Sharded类对这些实例变量的定义如下所示:
public static final int DEFAULT_WEIGHT = 1; private TreeMap<Long, S> nodes; private final Hashing algo; private final Map<ShardInfo<R>, R> resources = new LinkedHashMap<ShardInfo<R>, R>(); // 用于提取密钥标签的默认模式。 该模式必须有一个组(在圆括号之间),它将标签分隔开来进行散列。 空模式可以避免为每个查找应用正则表达式,因此不会使用关键标记来改进性能。 private Pattern tagPattern = null; public static final Pattern DEFAULT_KEY_TAG_PATTERN = Pattern .compile("\\{(.+?)\\}");
接下来看其构造函数中的initialize方法
可以看到,它对每一个ShardInfo通过一定规则计算其哈希值,然后存到TreeMap中,这里它实现了一致性哈希算法中虚拟节点的概念,因为我们可以看到同一个ShardInfo不止一次被放到TreeMap中,数量是,权重*160。
增加了虚拟节点的一致性哈希有很多好处,比如能避免数据在redis节点间分布不均匀
然后,在LinkedHashMap中放入ShardInfo以及其对应的Jedis实例,通过调用其自身的createSource()来得到jedis实例
数据定位
从ShardedJedis的代码中可以看到,无论进行什么操作,都要先根据key来找到对应的Redis,然后返回一个可供操作的Jedis实例。
例如其set方法
而getShard方法则在Sharded.java中实现,其源代码如下所示:
可以看到,先通过getShardInfo方法从TreeMap中获得对应的ShardInfo,然后根据这个ShardInfo就能够再LinkedHashMap中获得对应的Jedis实例了
作者:芥末无疆sss
链接:https://www.jianshu.com/p/d1fc9ab63e16
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
共同學習,寫下你的評論
評論加載中...
作者其他優質文章