redis是一个著名的key-value存储系统,也是nosql中的最常见的一种。其实,个人认为,redis最强大的地方不在于其存储,而在于其强大的缓存作用。

我们可以把它想象成一个巨大的(多借点集群,聚合多借点的内存)的Map,也就是Key-Value。

所以,我们可以把它做成缓存组件。

官方推荐的java版客户端是jedis,非常强大和稳定,支持事务、管道及有jedis自身实现。我们对redis数据的操作,都可以通过jedis来完成。

更多redis的概念,请参考:Redis集群(Redis3.0)

那我们就来看一看,jedis不同的调用方式:

(1)普通同步方式

这是一种最简单和最基础的调用方式,对于简单的数据存取需求,我们可以通过这种方式调用。

  1. public void jedisNormal() {
  2. Jedis jedis = new Jedis("localhost");
  3. long start = System.currentTimeMillis();
  4. for (int i = 0; i < 100000; i++) {
  5. String result = jedis.set("n" + i, "n" + i);
  6. }
  7. long end = System.currentTimeMillis();
  8. System.out.println("Simple SET: " + ((end - start) / 1000.0) + " seconds");
  9. jedis.disconnect();
  10. }
  11.  
  12. //每次set之后都可以返回结果,标记是否成功。

(2)事务方式(Transactions)

所谓事务,即一个连续操作,是否执行是一个事务,要么完成,要么失败,没有中间状态。

而redis的事务很简单,他主要目的是保障,一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令,也就是事务的连贯性。

  1. public void jedisTrans() {
  2. Jedis jedis = new Jedis("localhost");
  3. long start = System.currentTimeMillis();
  4. Transaction tx = jedis.multi();
  5. for (int i = 0; i < 100000; i++) {
  6. tx.set("t" + i, "t" + i);
  7. }
  8. List<Object> results = tx.exec();
  9. long end = System.currentTimeMillis();
  10. System.out.println("Transaction SET: " + ((end - start) / 1000.0) + " seconds");
  11. jedis.disconnect();
  12. }
  13.  
  14. //我们调用jedis.watch(…)方法来监控key,如果调用后key值发生变化,则整个事务会执行失败。另外,事务中某个操作失败,并不会回滚其他操作。这一点需要注意。还有,我们可以使用discard()方法来取消事务。

(3)管道(Pipelining)

管道是一种两个进程之间单向通信的机制。

那再redis中,为何要使用管道呢?有时候,我们需要采用异步的方式,一次发送多个指令,并且,不同步等待其返回结果。这样可以取得非常好的执行效率。

  1. public void jedisPipelined() {
  2. Jedis jedis = new Jedis("localhost");
  3. Pipeline pipeline = jedis.pipelined();
  4. long start = System.currentTimeMillis();
  5. for (int i = 0; i < 100000; i++) {
  6. pipeline.set("p" + i, "p" + i);
  7. }
  8. List<Object> results = pipeline.syncAndReturnAll();
  9. long end = System.currentTimeMillis();
  10. System.out.println("Pipelined SET: " + ((end - start) / 1000.0) + " seconds");
  11. jedis.disconnect();
  12. }

(4)管道中调用事务

对于,事务以及管道,这两个概念我们都清楚了。

在某种需求下,我们需要异步执行命令,但是,又希望多个命令是有连续的,所以,我们就采用管道加事务的调用方式。jedis是支持在管道中调用事务的。

  1. public void jedisCombPipelineTrans() {
  2. jedis = new Jedis("localhost");
  3. long start = System.currentTimeMillis();
  4. Pipeline pipeline = jedis.pipelined();
  5. pipeline.multi();
  6. for (int i = 0; i < 100000; i++) {
  7. pipeline.set("" + i, "" + i);
  8. }
  9. pipeline.exec();
  10. List<Object> results = pipeline.syncAndReturnAll();
  11. long end = System.currentTimeMillis();
  12. System.out.println("Pipelined transaction: " + ((end - start) / 1000.0) + " seconds");
  13. jedis.disconnect();
  14. }
  15. //效率上可能会有所欠缺

(5)分布式直连同步调用

这个是分布式直接连接,并且是同步调用,每步执行都返回执行结果。类似地,还有异步管道调用。

其实就是分片。

  1. public void jedisShardNormal() {
  2. List<JedisShardInfo> shards = Arrays.asList(
  3. new JedisShardInfo("localhost", 6379),
  4. new JedisShardInfo("localhost", 6380));
  5. ShardedJedis sharding = new ShardedJedis(shards);
  6. long start = System.currentTimeMillis();
  7. for (int i = 0; i < 100000; i++) {
  8. String result = sharding.set("sn" + i, "n" + i);
  9. }
  10. long end = System.currentTimeMillis();
  11. System.out.println("Simple@Sharing SET: " + ((end - start) / 1000.0) + " seconds");
  12. sharding.disconnect();
  13. }

(6)分布式直连异步调用

  1. public void jedisShardpipelined() {
  2. List<JedisShardInfo> shards = Arrays.asList(
  3. new JedisShardInfo("localhost", 6379),
  4. new JedisShardInfo("localhost", 6380));
  5. ShardedJedis sharding = new ShardedJedis(shards);
  6. ShardedJedisPipeline pipeline = sharding.pipelined();
  7. long start = System.currentTimeMillis();
  8. for (int i = 0; i < 100000; i++) {
  9. pipeline.set("sp" + i, "p" + i);
  10. }
  11. List<Object> results = pipeline.syncAndReturnAll();
  12. long end = System.currentTimeMillis();
  13. System.out.println("Pipelined@Sharing SET: " + ((end - start) / 1000.0) + " seconds");
  14. sharding.disconnect();
  15. }

(7)分布式连接池同步调用

如果,你的分布式调用代码是运行在线程中,那么上面两个直连调用方式就不合适了,因为直连方式是非线程安全的,这个时候,你就必须选择连接池调用。

连接池的调用方式,适合大规模的redis集群,并且多客户端的操作。

  1. public void jedisShardSimplePool() {
  2. List<JedisShardInfo> shards = Arrays.asList(
  3. new JedisShardInfo("localhost", 6379),
  4. new JedisShardInfo("localhost", 6380));
  5. ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
  6. ShardedJedis one = pool.getResource();
  7. long start = System.currentTimeMillis();
  8. for (int i = 0; i < 100000; i++) {
  9. String result = one.set("spn" + i, "n" + i);
  10. }
  11. long end = System.currentTimeMillis();
  12. pool.returnResource(one);
  13. System.out.println("Simple@Pool SET: " + ((end - start) / 1000.0) + " seconds");
  14. pool.destroy();
  15. }

(8)分布式连接池异步调用

  1. public void jedisShardPipelinedPool() {
  2. List<JedisShardInfo> shards = Arrays.asList(
  3. new JedisShardInfo("localhost", 6379),
  4. new JedisShardInfo("localhost", 6380));
  5. ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
  6. ShardedJedis one = pool.getResource();
  7. ShardedJedisPipeline pipeline = one.pipelined();
  8. long start = System.currentTimeMillis();
  9. for (int i = 0; i < 100000; i++) {
  10. pipeline.set("sppn" + i, "n" + i);
  11. }
  12. List<Object> results = pipeline.syncAndReturnAll();
  13. long end = System.currentTimeMillis();
  14. pool.returnResource(one);
  15. System.out.println("Pipelined@Pool SET: " + ((end - start) / 1000.0) + " seconds");
  16. pool.destroy();
  17. }

(9)需要注意的地方

事务和管道都是异步模式。在事务和管道中不能同步查询结果。比如下面两个调用,都是不允许的:

  1. Transaction tx = jedis.multi();
  2. for (int i = 0; i < 100000; i++) {
  3. tx.set("t" + i, "t" + i);
  4. }
  5.  
  6. System.out.println(tx.get("t1000").get()); //不允许
  7. List<Object> results = tx.exec();
  8.  
  9.  
  10.  
  11. Pipeline pipeline = jedis.pipelined();
  12. long start = System.currentTimeMillis();
  13. for (int i = 0; i < 100000; i++) {
  14. pipeline.set("p" + i, "p" + i);
  15. }
  16.  
  17. System.out.println(pipeline.get("p1000").get()); //不允许
  18. List<Object> results = pipeline.syncAndReturnAll();

事务和管道都是异步的,个人感觉,在管道中再进行事务调用,没有必要,不如直接进行事务模式。

分布式中,连接池的性能比直连的性能略好(见后续测试部分)。

分布式调用中不支持事务。

因为事务是在服务器端实现,而在分布式中,每批次的调用对象都可能访问不同的机器,所以,没法进行事务。

(10)总结

分布式中,连接池方式调用不但线程安全外,根据上面的测试数据,也可以看出连接池比直连的效率更好。

经测试分布式中用到的机器越多,调用会越慢。

(11)完整的测试代码

  1. package com.blogchong.example.nosqlclient;
  2. import java.util.Arrays;
  3. import java.util.List;
  4. import org.junit.AfterClass;
  5. import org.junit.BeforeClass;
  6. import org.junit.Test;
  7. import redis.clients.jedis.Jedis;
  8. import redis.clients.jedis.JedisPoolConfig;
  9. import redis.clients.jedis.JedisShardInfo;
  10. import redis.clients.jedis.Pipeline;
  11. import redis.clients.jedis.ShardedJedis;
  12. import redis.clients.jedis.ShardedJedisPipeline;
  13. import redis.clients.jedis.ShardedJedisPool;
  14. import redis.clients.jedis.Transaction;
  15. import org.junit.FixMethodOrder;
  16. import org.junit.runners.MethodSorters;
  17.  
  18. /**
  19. * @Description: jedis的8种调用方式
  20. */
  21.  
  22. @FixMethodOrder(MethodSorters.NAME_ASCENDING)
  23.  
  24. public class TestJedis {
  25. private static Jedis jedis;
  26. private static ShardedJedis sharding;
  27. private static ShardedJedisPool pool;
  28.  
  29. @BeforeClass
  30. public static void setUpBeforeClass() throws Exception {
  31. List<JedisShardInfo> shards = Arrays.asList(
  32. new JedisShardInfo("localhost", 6379),
  33. new JedisShardInfo("localhost", 6379)); //使用相同的ip:port,仅作测试
  34. jedis = new Jedis("localhost");
  35. sharding = new ShardedJedis(shards);
  36. pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
  37. }
  38.  
  39. @AfterClass
  40. public static void tearDownAfterClass() throws Exception {
  41. jedis.disconnect();
  42. sharding.disconnect();
  43. pool.destroy();
  44. }
  45.  
  46. @Test
  47. public void jedisNormal() {
  48. long start = System.currentTimeMillis();
  49. for (int i = 0; i < 100000; i++) {
  50. String result = jedis.set("n" + i, "n" + i);
  51. }
  52. long end = System.currentTimeMillis();
  53. System.out.println("Simple SET: " + ((end - start) / 1000.0) + " seconds");
  54. }
  55.  
  56. @Test
  57. public void jedisTrans() {
  58. long start = System.currentTimeMillis();
  59. Transaction tx = jedis.multi();
  60. for (int i = 0; i < 100000; i++) {
  61. tx.set("t" + i, "t" + i);
  62. }
  63. //System.out.println(tx.get("t1000").get());
  64. List<Object> results = tx.exec();
  65. long end = System.currentTimeMillis();
  66. System.out.println("Transaction SET: " + ((end - start) / 1000.0) + " seconds");
  67. }
  68.  
  69. @Test
  70. public void jedisPipelined() {
  71. Pipeline pipeline = jedis.pipelined();
  72. long start = System.currentTimeMillis();
  73. for (int i = 0; i < 100000; i++) {
  74. pipeline.set("p" + i, "p" + i);
  75. }
  76. //System.out.println(pipeline.get("p1000").get());
  77. List<Object> results = pipeline.syncAndReturnAll();
  78. long end = System.currentTimeMillis();
  79. System.out.println("Pipelined SET: " + ((end - start) / 1000.0) + " seconds");
  80. }
  81.  
  82. @Test
  83. public void jedisCombPipelineTrans() {
  84. long start = System.currentTimeMillis();
  85. Pipeline pipeline = jedis.pipelined();
  86. pipeline.multi();
  87. for (int i = 0; i < 100000; i++) {
  88. pipeline.set("" + i, "" + i);
  89. }
  90. pipeline.exec();
  91. List<Object> results = pipeline.syncAndReturnAll();
  92. long end = System.currentTimeMillis();
  93. System.out.println("Pipelined transaction: " + ((end - start) / 1000.0) + " seconds");
  94. }
  95.  
  96. @Test
  97. public void jedisShardNormal() {
  98. long start = System.currentTimeMillis();
  99. for (int i = 0; i < 100000; i++) {
  100. String result = sharding.set("sn" + i, "n" + i);
  101. }
  102. long end = System.currentTimeMillis();
  103. System.out.println("Simple@Sharing SET: " + ((end - start) / 1000.0) + " seconds");
  104. }
  105.  
  106. @Test
  107. public void jedisShardpipelined() {
  108. ShardedJedisPipeline pipeline = sharding.pipelined();
  109. long start = System.currentTimeMillis();
  110. for (int i = 0; i < 100000; i++) {
  111. pipeline.set("sp" + i, "p" + i);
  112. }
  113. List<Object> results = pipeline.syncAndReturnAll();
  114. long end = System.currentTimeMillis();
  115. System.out.println("Pipelined@Sharing SET: " + ((end - start) / 1000.0) + " seconds");
  116. }
  117.  
  118. @Test
  119. public void jedisShardSimplePool() {
  120. ShardedJedis one = pool.getResource();
  121. long start = System.currentTimeMillis();
  122. for (int i = 0; i < 100000; i++) {
  123. String result = one.set("spn" + i, "n" + i);
  124. }
  125. long end = System.currentTimeMillis();
  126. pool.returnResource(one);
  127. System.out.println("Simple@Pool SET: " + ((end - start) / 1000.0) + " seconds");
  128. }
  129.  
  130. @Test
  131. public void jedisShardPipelinedPool() {
  132. ShardedJedis one = pool.getResource();
  133. ShardedJedisPipeline pipeline = one.pipelined();
  134. long start = System.currentTimeMillis();
  135. for (int i = 0; i < 100000; i++) {
  136. pipeline.set("sppn" + i, "n" + i);
  137. }
  138. List<Object> results = pipeline.syncAndReturnAll();
  139. long end = System.currentTimeMillis();
  140. pool.returnResource(one);
  141. System.out.println("Pipelined@Pool SET: " + ((end - start) / 1000.0) + " seconds");
  142. }
  143. }

参考修改自:http://www.blogways.net/blog/2013/06/02/jedis-demo.html

Redis客户端API操作 Jedis详解的更多相关文章

  1. 深入浅出—Redis集群的相关详解

    前言: 这篇文章主要介绍了Redis集群的相关,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值. 注意!要求使用的都是redis3.0以上的版本,因为3.0以上增加了red ...

  2. net平台下c#操作ElasticSearch详解

    net平台下c#操作ElasticSearch详解 ElasticSearch系列学习 ElasticSearch第一步-环境配置 ElasticSearch第二步-CRUD之Sense Elasti ...

  3. Linux中redis安装配置及使用详解

    Linux中redis安装配置及使用详解 一. Redis基本知识 1.Redis 的数据类型 字符串 , 列表 (lists) , 集合 (sets) , 有序集合 (sorts sets) , 哈 ...

  4. ElasticSearch-.net平台下c#操作ElasticSearch详解

    ElasticSearch系列学习 ElasticSearch第一步-环境配置 ElasticSearch第二步-CRUD之Sense ElasticSearch第三步-中文分词 ElasticSea ...

  5. 【python】redis基本命令和基本用法详解

    [python]redis基本命令和基本用法详解 来自http://www.cnblogs.com/wangtp/p/5636872.html 1.redis连接 redis-py提供两个类Redis ...

  6. c#操作ElasticSearch5详解

    c#操作ElasticSearch详解 ElasticSearch系列学习 ElasticSearch第一步-环境配置 ElasticSearch第二步-CRUD之Sense ElasticSearc ...

  7. ElasticSearch第五步-.net平台下c#操作ElasticSearch详解

    前面我们讲解了关于ElasticSearch的安装配置,以及CRUD 本章我将讲解怎么使用c#操作ElasticSearch. 首先你需要一定的技术储备,比如:asp.net webapi,mvc,j ...

  8. ASP.NET 操作Cookie详解 增加,修改,删除

    ASP.NET 操作Cookie详解 增加,修改,删除 Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密).定义于RFC2109.它 ...

  9. 在telnet下操作memcache详解(操作命令详解)

    这篇文章主要介绍了在telnet下操作memcache详解,telnet下的memcache操作命令详解,需要的朋友可以参考下 在定位问题.测试等时候经常需要对memcache的数据进行一些操作,但是 ...

随机推荐

  1. Oracle索引——位图索引

    1.语法create bitmap index index_name on 表名(字段);2.举个例子你就能明白了:如有表 test(id,name,address)数据(1,张三,大连)(2,李四, ...

  2. 利用servlet产生随机数,原理是获取Graphics对象进行绘图

    public class ResonpeRandomImgDemo extends HttpServlet { int width=100; int height=30; public void do ...

  3. 学SpringMVC收藏

     一个较完整的SpringMVC工程的配置 2014-01-22 17:17:25 标签:java spring springMVC 配置 springSecurity web.xml 原创作品,允许 ...

  4. JS实现等比例缩放图片

    JS实现等比例缩放图片 2014-01-19 21:57 by 龙恩0707, 40 阅读, 0 评论, 收藏, 编辑 JS实现等比例缩放图片 有时候我们前端页面只有500×500像素的宽和高的布局, ...

  5. SQLSERVER误删Windows登录用户

    SQLSERVER误删除了Windows登录用户验证方式使用Windows身份验证的解决方法   SQLSERVER误删Windows登录用户验证方式使用Windows身份验证的解决方法 今天看到这篇 ...

  6. C#中另辟蹊径解决JSON / XML互转的问题

    C#中另辟蹊径解决JSON / XML互转的问题 最近在一个POC的项目中要用到JSON和XML的相互转换, 虽然我知道很多类库如JSON.NET具备这种功能, 但是我还是另辟蹊径的使用Spider ...

  7. ASP.NET服务器端事件利用MARQUEE实现正在处理效果

    前言:ASP.NET同仁们应该都遇到过当触发一个比较耗时的服务器端事件时,页面会处在一个等待的状态(即假死状态),用户体验非常不好,很容易造成用户二次点击,造成重复提交.至于解决方案自然是有的(问go ...

  8. linq to NHibernate

      什么是linq to NHibernate 什么是linq to NHibernate?说简单一点就是linq + NHibernate. linq语句是.Net 3.5中新增的功能,从问世以来就 ...

  9. ssdt_hook NtOpenProcess

        获取ssdt表中所有函数的地址 for (int i = 0; i < KeServiceDescriptorTable->NumberOfServices; i++) {     ...

  10. 搜索广告与广告网络Demand技术-搜索广告

    搜索广告 搜索广告就是一个典型的Ad Network,但是搜索广告非常重要,它的收入非常高,所以它有其独特之处,复杂度也比展示广告要高.它与展示广告在点击率预测,检索部分差不多,它的特点:1. 用户定 ...