当信息量较大时,我们就需要将信息保存在多台机器上。如何均匀分配数据呢?
redis.clients.jedis.ShardedJedisPool.java 为我们提供了一个简单一样的数据分箱的实现,下面分析一下其原理。
从构造方法入手:
- public ShardedJedisPool(final GenericObjectPool.Config poolConfig,
- List<JedisShardInfo> shards, Hashing algo, Pattern keyTagPattern) {
- super(poolConfig, new ShardedJedisFactory(shards, algo, keyTagPattern));
- }
poolConfig:同JedisPool的设置,参见《常见JedisConnectionException异常分析》http://blog.csdn.net/fachang/article/details/7984123
ShardedJedisFactory:继承自org.apache.commons.pool.BasePoolableObjectFactory<T>.java类,提供了为连接池创建连接实例的抽象实现,api如下:
api地址:http://commons.apache.org/pool/apidocs/index.html?org/apache/commons/pool/impl/GenericObjectPool.html
- voidactivateObject(T obj) No-op.
- voiddestroyObject(T obj) No-op.
- abstract T makeObject() Creates an instance that can be served by the pool.
- voidpassivateObject(T obj) No-op.
- booleanvalidateObject(T obj) This implementation always returns true.
其中我们需要关心的是ShardedJedisFactory对makeObject()抽象方法的实现,源码如下:
- public Object makeObject() throws Exception {
- ShardedJedis jedis = new ShardedJedis(shards, algo, keyTagPattern);
- return jedis;
- }
接着探寻redis.clients.jedis.ShardedJedis extends BinaryShardedJedis类的构造方法:
- public ShardedJedis(List<JedisShardInfo> shards, Hashing algo, Pattern keyTagPattern) {
- super(shards, algo, keyTagPattern);
- }
调用了父类redis.clients.jedis.BinaryShardedJedis extends Sharded<Jedis, JedisShardInfo>的构造方法,源码如下:
- public BinaryShardedJedis(List<JedisShardInfo> shards, Hashing algo, Pattern keyTagPattern) {
- super(shards, algo, keyTagPattern);
- }
接着看其父类redis.clients.util.Sharded<R, S extends ShardInfo<R>>的构造方法:
连接池存储信息:
存储连接池pool中各个连接信息JedisShardedInfo,实际操作中一个JedisShardedInfo根据其名字,对应160个沙箱孔(key),这样沙箱孔越多,数据将分布越均匀。
private TreeMap<Long, S> nodes;
存储连接信息JedisShardedInfo到实际连接实例ShardedJedis的映射。
- private final Map<ShardInfo<R>, R> resources = new LinkedHashMap<ShardInfo<R>, R>();
- public Sharded(List<S> shards, Hashing algo, Pattern tagPattern) {
- this.algo = algo;
- this.tagPattern = tagPattern;
- initialize(shards);
- }
真正的分校操作应该就是initialize(List<S> shards)方法了,源码如下:
- private void initialize(List<S> shards) {
- nodes = new TreeMap<Long, S>();
- for (int i = 0; i != shards.size(); ++i) {
- final S shardInfo = shards.get(i);
- 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);
- }
- resources.put(shardInfo, shardInfo.createResource());
- }
- }
其中我们看出JedisShardedPool通过每一个JedisShardedInfo配置的连接的name属性类分箱。具体做法是,每个Redis连接根据其名字+权重+计数号等信息
进行160次哈希计算作为160个 “沙箱孔”key(一个Long值)都对应一个redis连接信息JedisShardedInfo实例。假设有2个redis连接信息如下:
- List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
- shards.add(new JedisShardInfo("localhost", 6379, "master1"));
- shards.add(new JedisShardInfo("localhost", 6380, "master2"));
则:
名字为master1,端口为6379的连接在pool中对应根据名字master1+权重+计数号生成的160个沙箱孔。
名字为master2,端口为6380的连接在pool中对应根据名字master2+权重+计数号生成的160个沙箱孔.
注:为什么会生成160个呢?个人认为这个数字是考虑到redis key 值能在各连接中均匀分布而为之。
这样当我们通过key对redis进行操作时,会用同样的hash算法对该key进行hash操作,然后在所有的沙箱孔(连接的keys)中找到那个力该key值最接近的那个孔(连接在pool中的key值),
获取 redis连接,进行相应的操作。
举个例子,进行如下操作:
- ShardedJedis jedis = shardedJedisPool.getResource();
- jedis.set("key1", "value1");
看ShardedJedis.java源码如下:
- public String set(String key, String value) {
- Jedis j = getShard(key);
- return j.set(key, value);
- }
首先要调用getShard(key)方法获取当前要操作的key对应的连接实例,继续看getShard()方法源码:
- public R getShard(String key) {
- return resources.get(getShardInfo(key));
- }
为从resources中获取连接实例,需要知道该连接实例在reeMap<Long, S> nodes 中对应哪个沙箱孔?
- public S getShardInfo(byte[] key) {
- SortedMap<Long, S> tail =nodes.tailMap(algo.hash(key));
- if (tail.isEmpty()) {
- return nodes.get(nodes.firstKey());
- }
- return tail.get(tail.firstKey());
- }
- public S getShardInfo(String key) {
- return getShardInfo(SafeEncoder.encode(getKeyTag(key)));
- }
重点在getShardInfo(byte[] key)的实现,根据key的hash值,找到nodes中比它大的key值(沙箱孔)。如果没有,则拿第一个孔,如果存在则取比它大的第一个孔。
这样对key的save/query类操作都能映射到沙箱中的同一个孔,获取相同的连接,保证数据的一致和完整性。
以上是Jedis沙盒机制的默认实现,但在实际应用中我们不一定要采用这样的机制。
相关推荐
redis实现分布式锁(java/jedis),其中包含工具方法以及使用demo 本资源是利用java的jedis实现 redis实现分布式锁(java/jedis),其中包含工具方法以及使用demo 本资源是利用java的jedis实现
### Java Redis 使用之利用 Jedis 实现 Redis 消息队列 #### 一、引言 随着互联网应用的发展,消息队列在系统架构中的地位愈发重要。它不仅可以提高系统的响应速度,还可以帮助开发者构建出更加健壮、可扩展的应用...
Jedis 是 Redis 官方首选的 Java 客户端开发包。 实例方法: ? 1 import redis.clients.jedis.* ? 1 2 3 Jedis jedis = new Jedis("localhost"); jedis.set("foo", "bar"); String value = jedis.get("foo"); 支持...
使用Jedis,开发者可以轻松实现以下功能: 1. 数据缓存:利用Redis的高速读写能力,存储和检索频繁访问的数据,提升应用性能。 2. 分布式锁:通过设置和删除特定键来实现分布式锁,确保在多线程或多节点环境下对...
通过对Jedis 3.0.0源码的深入分析,我们可以看到其在连接管理、命令执行、集群支持、管道模式、数据类型操作和异常处理等方面的设计和实现。这些特性使得Jedis成为了Java开发者与Redis交互的首选客户端。理解并掌握...
要开始使用 Jedis,首先将其作为依赖项添加到您的 Java 项目中。如果使用 Maven: ...Jedis实例实现大多数 Redis 命令。 使用连接池的更简单方法: JedisPooled jedis = new JedisPooled("localhost", 6379);
2. **jedis-2.7.0.jar**:这是Jedis的主要库文件,包含了Jedis的所有API和实现。2.7.0是Jedis的一个特定版本,可能包含了对Redis协议的支持,各种命令的实现,以及一些优化和bug修复。使用这个版本的Jedis,开发者...
源码揭示了这些功能的实现原理。 8. **pipeline和事务的异步优化**: 使用`Pipeline`可以批量发送命令,减少网络通信次数,提高性能。源码中,`Pipeline`类会收集命令并一次性发送,然后一次性解析所有响应。 9. ...
**Jedis:Redis的Java客户端** Jedis是Java开发者用于操作Redis数据库的首选客户端库。Redis是一个开源的、高性能的键值对存储系统,常被用作数据缓存、消息中间件以及数据库。Jedis提供了丰富的API,允许开发人员...
jedis5.1.0.jar
jedis-2.9.0.jar jedis-2.9.0 jar 包,不包含源码,源码下载地址: http://download.csdn.net/download/tan3739/9993938 测试代码: 导入依赖包: commons-lang-2.5.jar commons-pool2-2.4.2.jar jedis-2.9.0 jar ...
在Java环境中与Redis进行交互,我们通常会使用Jedis这个客户端库。Jedis提供了丰富的API,能够帮助开发者轻松完成各种Redis操作,如设置和获取键值、执行事务、订阅/发布消息等。 Jedis的主要功能包括: 1. 基本...
### 使用Redisson替代Jedis 在分布式系统中,Redis作为一种高性能的键值存储数据库,被广泛应用于缓存、消息队列、数据同步等场景。在Java开发领域,开发者可以选择多种客户端来与Redis进行交互,其中最常用的是...
在本文中,我们将深入探讨 Jedis 的单机版和集群版使用,以及如何利用它来实现各种 Redis 操作。 ### 单机版 Jedis 使用 在单机版 Redis 设置中,Jedis 作为客户端直接与单个 Redis 服务器进行通信。以下是一些...
4. **发布/订阅**:Jedis提供了`subscribe()`和`publish()`方法,用于实现Redis的消息发布与订阅功能,从而构建基于消息的分布式系统。 5. **Pipeline**:为了提高性能,Jedis提供了管道模式,允许一次性发送多条...
Jedis是Java开发的一款高效的Redis客户端库,专为处理Redis数据结构而设计。在本文中,我们将深入探讨Jedis的安装、配置、基本操作以及它在实际应用中的重要性。 一、Jedis简介 Jedis是由Xavier Lachier开发的Java...
3. **jedis-3.0.0-sources.jar**:这个文件包含了Jedis库的源代码,对于想要深入理解Jedis内部实现或者进行二次开发的开发者来说,这是一个非常宝贵的资源。通过阅读源代码,可以了解Jedis如何封装Redis协议,如何...
Jedis API中文使用文档详解 Jedis 是 Redis 官方首选的 Java 客户端开发包,用于操作 Redis 数据库。下面是 Jedis API 的详细使用文档,适合新手和老程序员进行复习。 Jedis 的基本使用 Jedis 提供了多种方式来...
`Jedis`类实现了连接的创建、管理以及命令的发送和接收。`Connection`类中包含了TCP套接字操作和协议解析的相关逻辑。 2. **Commands** - Jedis提供了许多接口,如`StringCommands`、`HashCommands`等,每个接口...