事务
Ø MULTI,EXEC操作:事务的开始和结束标记
n 执行Multi命令,redis反馈ok表示开始事务;执行EXEC命令,结束事务,开始顺序执行事务中的操作。
127.0.0.1:6379[5]> MULTI
OK
127.0.0.1:6379[5]> set k1 1
QUEUED
127.0.0.1:6379[5]> set k2 2
QUEUED
127.0.0.1:6379[5]> exec
1) OK
2) OK
127.0.0.1:6379[5]> get k1
"1"
127.0.0.1:6379[5]> get k2
"2"
错误处理
语法错误:执行命令不存在或参数不对,都是语法错误,只有事务中有一处错误,redis执行exec命令后,会直接返回错误,正确的命令也不会被执行。
127.0.0.1:6379[5]> multi
OK
127.0.0.1:6379[5]> set name zhangsan
QUEUED
127.0.0.1:6379[5]> sss age 10
(error) ERR unknown command 'sss'
127.0.0.1:6379[5]> set sex nan
QUEUED
127.0.0.1:6379[5]> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379[5]> get name
(nil)
127.0.0.1:6379[5]> get sex
(nil)
运行错误:此类错误执行命令没错,但是操作对应的键有误,会造成运行时才会发现的错误,但是不影响其它正确命令的执行。
127.0.0.1:6379[5]> multi
OK
127.0.0.1:6379[5]> set k 10
QUEUED
127.0.0.1:6379[5]>sadd k 20//使用散列集合的命令操作strings类型数据
QUEUED
127.0.0.1:6379[5]> set k 100
QUEUED
127.0.0.1:6379[5]> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK
127.0.0.1:6379[5]> get k
"100"
WATCH命令
监控指定键,若被监控的键在以下事务执行前被修改,则包含对此键有操作动作的事务不执行,返回nil。
通俗的来讲就是。当你一旦开始watch一个key的时候,开启了一个事务,那么在这个事务提交之前数据发生改变,那么就事务执行失败。从下面的例子就可以看出。
127.0.0.1:6379[5]> get k
"20"
127.0.0.1:6379[5]> set k 30
OK
127.0.0.1:6379[5]>watch k
OK
127.0.0.1:6379[5]> set k 40
OK
127.0.0.1:6379[5]> multi
OK
127.0.0.1:6379[5]> set k 50
QUEUED
127.0.0.1:6379[5]> exec
(nil)
127.0.0.1:6379[5]> get k
"40"
java中redis的watch与Transaction功能使用
https://www.liangzl.com/get-article-detail-38409.html
非常好的一个文章。
需求:在项目中遇到了一个使用redis并希望原子性更改值的需要,记录一下。
设置key值,如果传的value比redis中的value大则设置传入的value
/**
* @Description: value greater than get value then set value 设置key值,如果传的value比redis中的value大则设置传入的value
* @param key 需要设置的key
* @param value 需要设置的value
* @return: boolean
* @Author: wangtongxing
* @Date: 2019/1/11
*/
public static boolean vGtGetSet(String key, long value) {
String s = null;
Jedis jedis = null;
try {
jedis = JedisPoolUtils.getJedis();
List<Object> exec = null;
int i=0;
while ((exec == null || exec.size()==0 || !"OK".equals(exec.get(0))) && i<5){
jedis.watch(key);
String getS = jedis.get(key);
long get = 0;
if (StringUtils.isNotEmpty(getS)){
try {
get = Long.parseLong(getS);
}catch (Exception e){
e.printStackTrace();
}
}
if (value>get){
Transaction transaction = jedis.multi();//返回一个事务控制对象
transaction.set(key, Long.toString(value));//预先在事务对象中装入要执行的操作
transaction.expire(key, CACHE_6_MONTHS);
exec = transaction.exec();//执行
}else {
jedis.unwatch();
return true;
}
i++;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JedisPoolUtils.returnRes(jedis);
}
return false;
}
使用中需要注意的几个点:
1.在不成功的情况下,一般需要重试几次,在重试的过程中每次循环都需要重新watch操作,因为每次事务提交之后,watch操作都会失效。
2.在事务提交之后返回的结果对象分为几种情况
事务提交前,watch的key发生改变,返回的List对象并不是null,而是一个初始化后的空对象(size==0)
事务提交前,watch的key没有改变,事务提交成功,返回的List对象中有一个”OK”的String对象。