Redis系列学习(三)之java api接入及事务
一、 引言
想把redis数据库接入java,首先官方推荐的jedis框架,里面包含了redis的基本操作以及命令行操作。极大的方便了我们的工作。
git地址
maven的pom.xml引用:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
二、 先上个简单例子
例子里包括了上篇文章学习到的所以的命令行操作对应的APi方法,由于我们已经学习了命令行操作redis数据库的方法,这里时候再研究API,我们相信就没那么难了。
例子
package com.sea;
import java.util.HashMap;
import java.util.Map;
import redis.clients.jedis.BinaryClient;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisClient {
// 客户端连接
private Jedis jedis;
// 客户端连接池
private JedisPool jedisPool;
public RedisClient() {
initialPool();
jedis = jedisPool.getResource();
}
/**
* 初始化
*/
private void initialPool() {
// 池基本配置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(20);
config.setMaxIdle(5);
config.setMaxWaitMillis(1000l);
config.setTestOnBorrow(false);
jedisPool = new JedisPool(config, "127.0.0.1", 6379);
}
public void show() {
testOperate();
//释放资源
jedisPool.close();
}
private void testOperate() {
System.out.println("======================通用命令操作==========================");
jedis.keys("*");//查找所以的key
jedis.keys("test*");//查找以test开头的key
jedis.del("tt");//删除
jedis.exists("tt");//查询key是否存在
jedis.expire("name", 60);//设置过期时间,单位秒
jedis.ttl("name");//查看key离超时还剩的时间
jedis.type("name");//查看key的数据类型
jedis.flushDB();// 清空数据
jedis.auth("123456");//设置连接密码
System.out.println("======================String命令操作==========================");
//增加
jedis.set("name", "chenyuan");
jedis.set("age", "100");
System.out.println("测试字符串添加:" + jedis.get("name"));
//修改
jedis.set("name", "chenyuan");
jedis.append("name", "33");//追加
jedis.incr("age");//加1
jedis.decr("age");//减1
jedis.incrBy("age", 6);//加指定值
jedis.decrBy("age", 6);//减指定值
System.out.println("测试字符串修改:" + jedis.get("name"));
//删除
jedis.del("name");//单条删除
jedis.del("name","age");//多条删除
System.out.println("测试字符串删除:" + jedis.get("name"));
System.out.println("======================hash命令操作==========================");
//增加
jedis.hset("testHash", "counrty", "china");//向testHash表里添加一条记录
Map<String,String> data = new HashMap<String, String>();
data.put("sex", "man");
data.put("add", "nibo");
jedis.hmset("testHash", data);//向testHash表里添加多条记录
//修改
jedis.hset("testHash", "counrty", "china1");//直接覆盖值
jedis.hset("testHash", "age", "999");//直接覆盖值
jedis.hincrBy("testHash", "age", 1);//增加指定值
//查找
jedis.hget("testHash", "counrty");//查找
//扩展命令
jedis.hexists("testHash", "counrty");//判断testHash表里是否存在key是counrty的元素
jedis.hlen("testHash");//表长度
jedis.hkeys("testHash");//获得所以keys
jedis.hvals("testHash");//获得所以values
//删除
jedis.hdel("testHash", "counrty");//删除单条记录
jedis.hdel("testHash", "counrty","age");//删除多条记录,没有就忽略
System.out.println("======================list命令操作==========================");
//增加
jedis.lpushx("testList", "1,2,3,4,5,6,a,b,c,d,e,f");//向表插入数据,表不存在则忽略
jedis.lpush("testList", "1,2,3,4,5,6,a,b,c,d,e,f");//向表插入数据,表不存在就创建
jedis.linsert("testList", BinaryClient.LIST_POSITION.BEFORE, "e", "99");//在元素值是f的前面插入99
jedis.linsert("testList", BinaryClient.LIST_POSITION.AFTER, "f", "100");//在元素值是f的后面插入100
//删除
jedis.rpop("testList");//弹出
jedis.lrem("testList", 2, "d");//指令会删除count个值为value的元素; 如果count大于0则从头遍历;如果count小于0则从尾遍历;如果count等于0,删除链表中所有等于某个值的元素
//修改
jedis.lset("testList", 0, "aa");//修改指定索引号位置的值
//查询
jedis.lrange("testList", 0, 3);//查找某范围值
}
}
单一Jedis实例不是线程安全的。为了避免这些问题,可以使用JedisPool,JedisPool是一个线程安全的网络连接池。可以用JedisPool创建一些可靠Jedis实例,可以从池中拿到Jedis的实例。
三、 事务
事务即一个连续的,不会被其它进程打断的状态,要么一起成功,要么一起失败。保证数据的一致和完整性。
redis的事务确保命令会有序的,连续的执行,中间不会被其它client的命令串插。
redis事务中的所有命令都会序列化、按顺序地执行,在事务执行的过程中,redis不会在为其他的数据库客户端提供任何的服务,从而保证事务中所有的命令都被原子化,而且事务中的命令要么全部被执行,要么全部都不执行。
开启事务:multi
提交事务:exec
回滚命令:discard(清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态)
添加监视:watch(key),监视某个key的变化,如果在事务执行之前这个(或这些) key被其他命令所改动,那么会取消事务里全部命令。
取消监视:unwatch(key)
与java api对应的方法如下:
//开户事务,返回事务对象
Transaction transaction = jedis.multi();
//提交事务
transaction.exec();
//回滚
transaction.discard();
//监视key值的变化
transaction.watch("name");
当输入MULTI命令后,服务器返回OK表示事务开始成功,然后依次输入需要在本次事务中执行的所有命令,每次输入一个命令服务器并不会马上执行,而是返回”QUEUED”,这表示命令已经被服务器接受并且暂时保存起来,最后输入EXEC命令后,本次事务中的所有命令才会被依次执行,可以看到最后服务器一次性返回了三个OK,这里返回的结果与发送的命令是按顺序一一对应的,这说明这次事务中的命令全都执行成功了。
总结:开户事务后,后面的命令会加入队列中直接调用exec提交事务才开始执行。redis保证事务2点重要性:1、事务里的命令要么全部执行,要么全部不执行。2、事务执行命令添加的按顺序执行,中间不会乱序,也不会穿插其它客户端命令。
事务的错误处理机制
1、语法错误。命令不存在或参数错误的话,这在执行exec命令之前就知道了,因为如果正确加入到命令队列里会返回“QUEUED”,否则Redis将返回一个错误,如果Redis 2.6.5之前的版本会忽略错误的命令,执行其他正确的命令,2.6.5之后的版本会忽略这个事务中的所有命令,都不执行。
2、运行错误。运行时发生的错误,即使错误发生(redis接收并执行,并没有把它当作错误来处理),后面的命令也会继续执行。
注意:redis的回滚并非关系型数据库里“回滚”的意思,redis追求更快速,更高效的算法,它主动忽略了回滚错误的处理,它要程序员自己确保命令执行不会出错
总结下来。意思就是,redis支持事务处理,语法错误的情况下会确保命令全部执行或全部不执行,而且按添加顺序执行。在运行错误的情况下,redis会忽略错误命令,继续执行下面的命令。