当前位置:   article > 正文

深入剖析Redis客户端Jedis的特性和原理_shardedjedis原理及应用

shardedjedis原理及应用

一、开篇

Redis作为目前通用的缓存选型,因其高性能而倍受欢迎。Redis的2.x版本仅支持单机模式,从3.0版本开始引入集群模式。

Redis的Java生态的客户端当中包含Jedis、Redisson、Lettuce,不同的客户端具备不同的能力是使用方式,本文主要分析Jedis客户端。

Jedis客户端同时支持单机模式、分片模式、集群模式的访问模式,通过构建Jedis类对象实现单机模式下的数据访问,通过构建ShardedJedis类对象实现分片模式的数据访问,通过构建JedisCluster类对象实现集群模式下的数据访问。

Jedis客户端支持单命令和Pipeline方式访问Redis集群,通过Pipeline的方式能够提高集群访问的效率。

本文的整体分析基于Jedis的3.5.0版本进行分析,相关源码均参考此版本。

二、Jedis访问模式对比

Jedis客户端操作Redis主要分为三种模式,分表是单机模式、分片模式、集群模式。

  • 单机模式主要是创建Jedis对象来操作单节点的Redis,只适用于访问单个Redis节点。
  • 分片模式(ShardedJedis)主要是通过创建ShardedJedisPool对象来访问分片模式的多个Redis节点,是Redis没有集群功能之前客户端实现的一个数据分布式方案,本质上是客户端通过一致性哈希来实现数据分布式存储。
  • 集群模式(JedisCluster)主要是通过创建JedisCluster对象来访问集群模式下的多个Redis节点,是Redis3.0引入集群模式后客户端实现的集群访问访问,本质上是通过引入槽(slot)概念以及通过CRC16哈希槽算法来实现数据分布式存储。

单机模式不涉及任何分片的思想,所以我们着重分析分片模式和集群模式的理念。

2.1 分片模式

  • 分片模式本质属于基于客户端的分片,在客户端实现如何根据一个key找到Redis集群中对应的节点的方案。
  • Jedis的客户端分片模式采用一致性Hash来实现,一致性Hash算法的好处是当Redis节点进行增减时只会影响新增或删除节点前后的小部分数据,相对于取模等算法来说对数据的影响范围较小。
  • Redis在大部分场景下作为缓存进行使用,所以不用考虑数据丢失致使缓存穿透造成的影响,在Redis节点增减时可以不用考虑部分数据无法命中的问题。

分片模式的整体应用如下图所示,核心在于客户端的一致性Hash策略。

2.2 集群模式

集群模式本质属于服务器分片技术,由Redis集群本身提供分片功能,从Redis 3.0版本开始正式提供。

集群的原理是:一个 Redis 集群包含16384 个哈希槽(Hash slot), Redis保存的每个键都属于这16384个哈希槽的其中一个, 集群使用公式CRC16(key)%16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键key的CRC16校验和 。

集群中的每个节点负责处理一部分哈希槽。举个例子, 一个集群可以有三个哈希槽, 其中:

  • 节点 A 负责处理 0 号至 5500 号哈希槽。
  • 节点 B 负责处理 5501 号至 11000 号哈希槽。
  • 节点 C 负责处理 11001 号至 16383 号哈希槽。

Redis在集群模式下对于key的读写过程首先将对应的key值进行CRC16计算得到对应的哈希值,将哈希值对槽位总数取模映射到对应的槽位,最终映射到对应的节点进行读写。以命令set("key", "value")为例子,它会使用CRC16算法对key进行计算得到哈希值28989,然后对16384进行取模得到12605,最后找到12605对应的Redis节点,最终跳转到该节点执行set命令。

集群模式的整体应用如下图所示,核心在于集群哈希槽的设计以及重定向命令。

(引用自:www.jianshu.com)

三、Jedis的基础用法

  1. // Jedis单机模式的访问
  2. public void main(String[] args) {
  3. // 创建Jedis对象
  4. jedis = new Jedis("localhost", 6379);
  5. // 执行hmget操作
  6. jedis.hmget("foobar", "foo");
  7. // 关闭Jedis对象
  8. jedis.close();
  9. }
  10. // Jedis分片模式的访问
  11. public void main(String[] args) {
  12. HostAndPort redis1 = HostAndPortUtil.getRedisServers().get(0);
  13. HostAndPort redis2 = HostAndPortUtil.getRedisServers().get(1);
  14. List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>(2);
  15. JedisShardInfo shard1 = new JedisShardInfo(redis1);
  16. JedisShardInfo shard2 = new JedisShardInfo(redis2);
  17. // 创建ShardedJedis对象
  18. ShardedJedis shardedJedis = new ShardedJedis(shards);
  19. // 通过ShardedJedis对象执行set操作
  20. shardedJedis.set("a", "bar");
  21. }
  22. // Jedis集群模式的访问
  23. public void main(String[] args) {
  24. // 构建redis的集群池
  25. Set<HostAndPort> nodes = new HashSet<>();
  26. nodes.add(new HostAndPort("127.0.0.1", 7001));
  27. nodes.add(new HostAndPort("127.0.0.1", 7002));
  28. nodes.add(new HostAndPort("127.0.0.1", 7003));
  29. // 创建JedisCluster
  30. JedisCluster cluster = new JedisCluster(nodes);
  31. // 执行JedisCluster对象中的方法
  32. cluster.set("cluster-test", "my jedis cluster test");
  33. String result = cluster.get("cluster-test");
  34. }

Jedis通过创建Jedis的类对象来实现单机模式下的数据访问,通过构建JedisCluster类对象来实现集群模式下的数据访问。

要理解Jedis的访问Redis的整个过程,可以通过先理解单机模式下的访问流程,在这个基础上再分析集群模式的访问流程会比较合适。

四、Jedis单机模式的访问

Jedis访问单机模式Redis的整体流程图如下所示,从图中可以看出核心的流程包含Jedis对象的创建以及通过Jedis对象实现Redis的访问。

熟悉Jedis访问单机Redis的过程,本身就是需要了解Jedis的创建过程以及执行Redis命令的过程。

  • Jedis的创建过程核心在于创建Jedis对象以及Jedis内部变量Client对象。
  • Jedis访问Redis的过程在于通过Jedis内部的Client对象访问Redis。

4.1 创建过程

Jedis本身的类关系图如下图所示,从图中我们能够看到Jedis继承自BinaryJedis类。

在BinaryJedis类中存在和Redis对接的Client类对象,Jedis通过父类的BinaryJedis的Client对象实现Redis的读写。

Jedis类在创建过程中通过父类BinaryJedis创建了Client对象,而了解Client对象是进一步理解访问过程的关键。

  1. public class Jedis extends BinaryJedis implements JedisCommands, MultiKeyCommands,
  2. AdvancedJedisCommands, ScriptingCommands, BasicCommands, ClusterCommands, SentinelCommands, ModuleCommands {
  3. protected JedisPoolAbstract dataSource = null;
  4. public Jedis(final String host, final int port) {
  5. // 创建父类BinaryJedis对象
  6. super(host, port);
  7. }
  8. }
  9. public class BinaryJedis implements BasicCommands, BinaryJedisCommands, MultiKeyBinaryCommands,
  10. AdvancedBinaryJedisCommands, BinaryScriptingCommands, Closeable {
  11. // 访问redis的Client对象
  12. protected Client client = null;
  13. public BinaryJedis(final String host, final int port) {
  14. // 创建Client对象访问redis
  15. client = new Client(host, port);
  16. }
  17. }

Client类的类关系图如下图所示,Client对象继承自BinaryClient和Connection类。在BinaryClient类中存在Redis访问密码等相关参数,在Connection类在存在访问Redis的socket对象以及对应的输入输出流。本质上Connection是和Redis进行通信的核心类。

Client类在创建过程中初始化核心父类Connection对象,而Connection是负责和Redis直接进行通信。

p
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/613152
推荐阅读
相关标签
  

闽ICP备14008679号