一、摘要:

  Redis是一个著名的key-value存储系统,而作为其官方推荐的java版客户端jedis也非常强大和稳定,支持事务、管道及有jedis自身实现的分布式。

二、五种数据类型的基本命令操作:

 package sy.test;

 import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map; import org.junit.Before;
import org.junit.Test; import redis.clients.jedis.Jedis;
import sy.util.RedisUtil; public class TestJedis {
private Jedis jedis; @Before
public void setUp() throws Exception {
//连接redis服务器,192.168.0.100:6379
jedis = new Jedis("192.168.0.16", 6379);
//登陆权限认证
jedis.auth("123456");
} /**
* redis存储字符串
*/
@Test
public void testString() {
jedis.set("name", "itme");
System.out.println(jedis.get("name")); jedis.append("name", " is my lover"); //拼接
System.out.println(jedis.get("name")); //删除某个键
jedis.del("name");
System.out.println(jedis.get("name")); //添加多个键值对
jedis.mset("name","mset","age","25","qq","123456");
System.out.println(jedis.get("name") + "-" + jedis.get("age") + "-" + jedis.get("qq")); //自增1操作
jedis.incr("age");
System.out.println(jedis.get("name") + "-" + jedis.get("age") + "-" + jedis.get("qq")); //关闭jedis连接
jedis.disconnect(); } /**
* 操作map
*/
@Test
public void testMap(){ //添加数据
Map<String, String> map = new HashMap<String,String>();
map.put("name", "xinxin");
map.put("age", "22");
map.put("qq", "123456"); jedis.hmset("user", map); List<String> list = jedis.hmget("user", "name","age","qq");
System.out.println(list); //删除数据
jedis.hdel("user", "name");
System.out.println(jedis.hmget("user", "age")); //因为删除了,所以返回的是null
System.out.println(jedis.hlen("user")); //返回key为user的键中存放的值的个数2
System.out.println(jedis.exists("user"));//是否存在key为user的记录 返回true
System.out.println(jedis.hkeys("user"));//返回map对象中的所有key
System.out.println(jedis.hvals("user"));//返回map对象中的所有value Iterator<String> iter=jedis.hkeys("user").iterator();
while (iter.hasNext()){
String key = iter.next();
System.out.println(key+":"+jedis.hmget("user",key));
} //关闭jedis连接
jedis.disconnect();
} /**
* 操作List
* 底层是双向链表
*/
@Test
public void testList(){
//开始前,先移除所有的内容
jedis.del("java framework");
System.out.println(jedis.lrange("java framework",0,-1));
//先向key java framework中存放三条数据
jedis.lpush("java framework","spring");
jedis.lpush("java framework","struts");
jedis.lpush("java framework","hibernate"); //再取出所有数据jedis.lrange是按范围取出,
// 第一个是key,第二个是起始位置,第三个是结束位置,jedis.llen获取长度 -1表示取得所有
System.out.println(jedis.lrange("java framework",0,-1)); jedis.del("java framework");
jedis.rpush("java framework","spring");
jedis.rpush("java framework","struts");
jedis.rpush("java framework","hibernate");
System.out.println(jedis.lrange("java framework",0,-1)); //关闭jedis连接
jedis.disconnect();
} /**
* jedis操作set
*/
@Test
public void testSet(){
//添加
jedis.sadd("user1","liuling");
jedis.sadd("user1","xinxin");
jedis.sadd("user1","ling");
jedis.sadd("user1","zhangxinxin");
jedis.sadd("user1","who");
//移除noname
jedis.srem("user1","who");
System.out.println(jedis.smembers("user1"));//获取所有加入的value
System.out.println(jedis.sismember("user1", "who"));//判断 who 是否是user集合的元素
System.out.println(jedis.srandmember("user1"));
System.out.println(jedis.scard("user1"));//返回集合的元素个数 //关闭jedis连接
jedis.disconnect();
} @Test
public void testSortList(){
//jedis 排序
//注意,此处的rpush和lpush是List的操作。是一个双向链表(但从表现来看的)
jedis.del("a");//先清除数据,再加入数据进行测试
jedis.rpush("a", "1");
jedis.lpush("a","6");
jedis.lpush("a","3");
jedis.lpush("a","9"); System.out.println(jedis.lrange("a",0,-1));// [9, 3, 6, 1]
//输入排序后结果 不影响原来的顺序。
System.out.println(jedis.sort("a")); //[1, 3, 6, 9]
System.out.println(jedis.lrange("a",0,-1)); //关闭jedis
jedis.disconnect();
} @Test
public void testRedisPool(){
RedisUtil.getJedis().set("newname", "中文测试");
System.out.println(RedisUtil.getJedis().get("newname"));
} }

三、Redis连接池

 package sy.util;

 import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig; public final class RedisUtil { //Redis服务器IP
private static String ADDR = "192.168.0.16"; //Redis的端口号
private static int PORT = 6379; //访问密码
private static String AUTH = "123456"; //可用连接实例的最大数目,默认值为8;
//如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
private static int MAX_ACTIVE = 1024; //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
private static int MAX_IDLE = 200; //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
private static int MAX_WAIT = 10000; private static int TIMEOUT = 10000; //在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
private static boolean TEST_ON_BORROW = true; private static JedisPool jedisPool = null; /**
* 初始化Redis连接池
*/
static {
try {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(MAX_ACTIVE);
config.setMaxIdle(MAX_IDLE);
config.setMaxWaitMillis(MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH);
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 获取Jedis实例
* @return
*/
public synchronized static Jedis getJedis() {
try {
if (jedisPool != null) {
Jedis resource = jedisPool.getResource();
return resource;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
} /**
* 释放jedis资源
* @param jedis
*/
public static void returnResource(final Jedis jedis) {
if (jedis != null) {
jedisPool.returnResource(jedis);
}
}
}

四、普通同步方式

  最简单和基础的调用方式:

 /**
* jedis redis普通同步方式
*/
@Test
public void testNormal(){
Jedis jedis = RedisUtil.getJedis();
long start = System.currentTimeMillis();
for(int i=0;i<10000;i++){
jedis.set("n" + i, "n" + i);
}
long end = System.currentTimeMillis();
System.out.println("Simple set" + ((end-start)/1000.0) + " seconds");
}

五、事务方式(Transactions)

  redis的事务很简单,他主要目的是保障,一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令。一般情况下 redis 在接受到一个 client 发来的命令后会立即处理并返回处理结果,但是当一个 client在一个连接中发 multi命令,这个连接会进入一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一个队列中。当从此连接受到 exec 命令后,redis会顺序的执行队列中的所有命令。并将所有命令的运行结果打包到一起返回给 client.然后此连接就结束事务上下文。

     @Test
public void testTransaction1(){
Jedis jedis = RedisUtil.getJedis();
long start = System.currentTimeMillis(); jedis.flushDB();
jedis.lpush("mylist", "hello"); //开启事务
Transaction tx = jedis.multi();
tx.set("testtran1", "testtran1"); tx.lset("mylist", 10, "world");
tx.set("testtran1-1", "testtran1-1");
//提交事务
List<Object> results = tx.exec(); long end = System.currentTimeMillis();
System.out.println(jedis.get("testtran1"));
System.out.println(jedis.lrange("mylist", 0, -1));
System.out.println(jedis.get("testtran1-1"));
System.out.println("Test Transaction1 " + ((end-start)/1000.0) + " seconds");
jedis.disconnect();
} @Test
public void testTransaction2(){
Jedis jedis = RedisUtil.getJedis();
long start = System.currentTimeMillis(); jedis.flushDB(); jedis.set("testtran2", "testtran2");
jedis.set("testtran2-1", "testtran2-1"); //开启事务
Transaction tx = jedis.multi();
tx.set("testtran2", "testtran2_new");
tx.set("testtran2-1", "testtran2-1_new"); //回滚事务
tx.discard(); long end = System.currentTimeMillis();
System.out.println(jedis.get("testtran2"));
System.out.println(jedis.get("testtran2-1"));
System.out.println("Test Transaction2 " + ((end-start)/1000.0) + " seconds");
jedis.disconnect();
}

  示例1说明,只要提交事务了,不管事务中的命令执行成功与否都会执行,即使有命令执行错误,也不会在java中抛出异常。其中tx.lset("mylist", 10, "world");的处理结果是错误的,但是其后边的命令继续执行。

  我们调用jedis.watch(…)方法来监控key,如果调用后key值发生变化,则整个事务会执行失败。另外,事务中某个操作失败,并不会回滚其他操作。这一点需要注意。还有,我们可以使用discard()方法来取消事务。

六、管道(Pipelining)

  有时,我们需要采用异步方式,一次发送多个指令,不同步等待其返回结果。批量发送请求,减少网络通信时间,最后将多条命令的执行结果打包返回给客户端。

/**
* 管道pipeline
*/
@Test
public void testPipeline(){
Jedis jedis = RedisUtil.getJedis();
jedis.flushDB(); //使用管道的方式
Pipeline pipeline = jedis.pipelined();
long start1 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipeline.set("pipeline" + i, "pipeline" + i);
}
long end1 = System.currentTimeMillis();
System.out.println("使用Pipeline的方式 " + ((end1-start1)/1000.0) + " seconds"); //普通方式
long start2 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipeline.set("normal" + i, "normal" + i);
}
long end2 = System.currentTimeMillis();
System.out.println("普通方式 " + ((end2-start2)/1000.0) + " seconds");
}

七、管道中调用事务

@Test
public void test4combPipelineTrans() {
jedis = new Jedis("192.168.0.16", 6379);long start = System.currentTimeMillis();
Pipeline pipeline = jedis.pipelined();
pipeline.multi();
for (int i = 0; i < 100000; i++) {
pipeline.set("" + i, "" + i);
}
pipeline.exec();
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
System.out.println("Pipelined transaction: " + ((end - start)/1000.0) + " seconds");
jedis.disconnect();
}

  但是经测试(见本文后续部分),发现其效率和单独使用事务差不多,甚至还略微差点。

八、分布式直连同步调用

 @Test
public void test5shardNormal() {
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("localhost",6379),
new JedisShardInfo("localhost",6380)); ShardedJedis sharding = new ShardedJedis(shards); long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
String result = sharding.set("sn" + i, "n" + i);
}
long end = System.currentTimeMillis();
System.out.println("Simple@Sharing SET: " + ((end - start)/1000.0) + " seconds"); sharding.disconnect();
}

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

九、分布式直连异步管道调用

 @Test
public void test6shardpipelined() {
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("localhost",6379),
new JedisShardInfo("localhost",6380)); ShardedJedis sharding = new ShardedJedis(shards); ShardedJedisPipeline pipeline = sharding.pipelined();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipeline.set("sp" + i, "p" + i);
}
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
System.out.println("Pipelined@Sharing SET: " + ((end - start)/1000.0) + " seconds"); sharding.disconnect();
}

十、分布式连接池同步调用

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

 @Test
public void test7shardSimplePool() {
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("localhost",6379),
new JedisShardInfo("localhost",6380)); ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards); ShardedJedis one = pool.getResource(); long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
String result = one.set("spn" + i, "n" + i);
}
long end = System.currentTimeMillis();
pool.returnResource(one);
System.out.println("Simple@Pool SET: " + ((end - start)/1000.0) + " seconds"); pool.destroy();
}

十一、分布式连接池异步调用

 @Test
public void test8shardPipelinedPool() {
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("localhost",6379),
new JedisShardInfo("localhost",6380)); ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards); ShardedJedis one = pool.getResource(); ShardedJedisPipeline pipeline = one.pipelined(); long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipeline.set("sppn" + i, "n" + i);
}
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
pool.returnResource(one);
System.out.println("Pipelined@Pool SET: " + ((end - start)/1000.0) + " seconds");
pool.destroy();
}

十二、需要注意的地方

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

 Transaction tx = jedis.multi();
for (int i = 0; i < 100000; i++) {
tx.set("t" + i, "t" + i);
}
System.out.println(tx.get("t1000").get()); //不允许 List<Object> results = tx.exec(); …
… Pipeline pipeline = jedis.pipelined();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipeline.set("p" + i, "p" + i);
}
System.out.println(pipeline.get("p1000").get()); //不允许 List<Object> results = pipeline.syncAndReturnAll();

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

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

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

 

 参考文章:

     1.http://my.oschina.net/sphl520/blog/312514#OSC_h3_3

      2.http://www.cnblogs.com/liuling/p/2014-4-19-04.html

      3.Redis实战《红丸出品》

      4.http://www.2cto.com/kf/201504/395403.html

  

Redis入门学习的更多相关文章

  1. redis入门学习记录(二)

    继第一节 redis入门学习记录(一)之后,我们来学习redis的基本使用. 接下来我们看看/usr/local/redis/bin目录下的几个文件作用是什么? redis-benchmark:red ...

  2. 反射实现Model修改前后的内容对比 【API调用】腾讯云短信 Windows操作系统下Redis服务安装图文详解 Redis入门学习

    反射实现Model修改前后的内容对比   在开发过程中,我们会遇到这样一个问题,编辑了一个对象之后,我们想要把这个对象修改了哪些内容保存下来,以便将来查看和追责. 首先我们要创建一个User类 1 p ...

  3. Redis入门学习(学习过程记录)

    Redis(入门笔记) 学习一个大的技术点,然后顺带着就把这个技术点的面试题给学习了. 学习完一个技术后,如果面试题还不能够解答的话,只能说明学的不精,需要查漏补缺. 下一个学习的方向:Redis-非 ...

  4. Redis入门学习(一)——安装配置

    最近马上要找实习了,听学长说他们公司里用了redis来解决缓存问题,在大三结束前来学习一下. 问:Redis是什么? 答:(官方)REmote DIctionary Server(Redis)是一个K ...

  5. Redis入门学习(二):下载安装

    Linux操作系统 Download, extract and compile Redis with: $ wget http://download.redis.io/releases/redis-4 ...

  6. Redis入门学习(一):简介

    Redis是一个开源的.高性能的.基于键值对的缓存与存储系统,通过提供多种键值数据类型来适应不同场景下的缓存与存储需求.同时Redis的诸多高层级功能使其可以胜任消息队列.任务队列等不同的角色. 20 ...

  7. Redis——入门学习笔记

    Redis学习 说到前面:这篇笔记只是我作为一个Redis新手,从0到认知的一个过程.后续会持续深入学习. 学习初衷和计划 学习Redis,因为这是热门技术,必须掌握的技术,别人都会我不会.就这一点就 ...

  8. Redis入门学习笔记一

    Redis 简要描述: 1.  Redis 是啥 ? Redis 英文名称全称为: Remote Dictionary Server ,中译为远程字典服务器. 是一款区分于磁盘数据库如(Mysql)的 ...

  9. redis入门学习记录(一)

    1.linux在线下载Redis ,官网地址:https://redis.io/download目前,最新的Redist版本为redis-5.0.0,使用wget下载 进入/usr/local/src ...

随机推荐

  1. TFS发布计划发送到钉钉消息群

    由于工作中需要用到钉钉,每天都要和钉钉打交道:上下班打卡.出差请假流程.各种工作讨论组,不一而足,工作已然和钉钉绑在了一起,难怪有广告词: 微信是一个生活方式,钉钉是一个工作方式. 我们是钉钉机器人内 ...

  2. 虚拟机Linux 的一些基础命令和注释

    cd命令 cd    ==回到初始,主目录 cd -  ==回到上一级目录交替 cd ~ ==回到root家目录 cd .  ==当前目录 cd .. ==进入上一级目录 ls命令 ls     == ...

  3. XJOI1689相连的城市

    相连的城市 n个城市中,某些城市间有道路互相连接.给出与每个城市相邻的城市有多少个,请输出城市间的邻接矩阵. 输入格式: 第一行输入一个正整数n,表示城市的个数. 第二行输入n个用空格隔开的非负整数, ...

  4. JSP的学习

    JSP的学习 1. (1).服务器的名字:Tomcat (2).服务器浏览器访问的地址为: http://localhost:8080 http://127.0.0.1:8080 2.简单的知识 (1 ...

  5. 用pl/sql游标实现约瑟夫环

    什么是约瑟夫环: 约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为1的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数, ...

  6. dotNet的体系结构介绍

    一.公共语言运行库 .NET Framework 的核心是其运行库执行环境,称为Common Language Run,通常在CLR控制下运行的代码称为托管代码(由GC进行资源管理和回收),还有一部分 ...

  7. MCDownloader(iOS下载器)说明书

    示例 前言 很多iOS应用中都需要下载数据,并对这些下载的过程和结果进行管理,因此我才有了写这个MCDownloader的想法.在IOS 文件下载器-MCDownloadManager这篇文章中,我使 ...

  8. /etc/postfix下 main.cf 配置文件详解

    postconf -n   #别名数据库(postalias/newaliases 命令生成) alias_database = hash:/etc/postfix/aliases   #别名表,列出 ...

  9. 实现一个竖直的显示表头的表格(vue版本)

    今天遇到一个问题,实现这样一个竖直的显示表头的表格,如下图.默认显示两列. vue实现代码如下:   tableComponent.vue:   <template> <table ...

  10. Twitter数据非API采集方法

    说明:这里分三个系列介绍Twitter数据的非API抓取方法. 在一个老外的博看上看到的,想详细了解的可以自己去看原文. 这种方法可以采集基于关键字在twitter上搜索的结果推文,已经实现自动翻页功 ...