之前一直了解redis中的事务机制,知道事务并没有真正的回滚,只是会中断,中断后的将不会执行,中断前的会生效。
使用是如果遇到希望保持原子性的更改,除了自增的简单操作,redis做的并不是很完善。
在项目中遇到了一个使用redis并希望原子性更改值的需要,记录一下。
/**
* @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对象。