每次事务提交之后,watch操作都会失效

事务

Ø MULTI,EXEC操作:事务的开始和结束标记
n 执行Multi命令,redis反馈ok表示开始事务;执行EXEC命令,结束事务,开始顺序执行事务中的操作。

  1. 127.0.0.1:6379[5]> MULTI
  2. OK
  3. 127.0.0.1:6379[5]> set k1 1
  4. QUEUED
  5. 127.0.0.1:6379[5]> set k2 2
  6. QUEUED
  7. 127.0.0.1:6379[5]> exec
  8. 1) OK
  9. 2) OK
  10. 127.0.0.1:6379[5]> get k1
  11. "1"
  12. 127.0.0.1:6379[5]> get k2
  13. "2"

错误处理

语法错误:执行命令不存在或参数不对,都是语法错误,只有事务中有一处错误,redis执行exec命令后,会直接返回错误,正确的命令也不会被执行。

  1. 127.0.0.1:6379[5]> multi
  2. OK
  3. 127.0.0.1:6379[5]> set name zhangsan
  4. QUEUED
  5. 127.0.0.1:6379[5]> sss age 10
  6. (error) ERR unknown command 'sss'
  7. 127.0.0.1:6379[5]> set sex nan
  8. QUEUED
  9. 127.0.0.1:6379[5]> exec
  10. (error) EXECABORT Transaction discarded because of previous errors.
  11. 127.0.0.1:6379[5]> get name
  12. (nil)
  13. 127.0.0.1:6379[5]> get sex
  14. (nil)

运行错误:此类错误执行命令没错,但是操作对应的键有误,会造成运行时才会发现的错误,但是不影响其它正确命令的执行。

  1. 127.0.0.1:6379[5]> multi
  2. OK
  3. 127.0.0.1:6379[5]> set k 10
  4. QUEUED
  5. 127.0.0.1:6379[5]>sadd k 20//使用散列集合的命令操作strings类型数据
  6. QUEUED
  7. 127.0.0.1:6379[5]> set k 100
  8. QUEUED
  9. 127.0.0.1:6379[5]> exec
  10. 1) OK
  11. 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
  12. 3) OK
  13. 127.0.0.1:6379[5]> get k
  14. "100"

WATCH命令

监控指定键,若被监控的键在以下事务执行前被修改,则包含对此键有操作动作的事务不执行,返回nil。

通俗的来讲就是。当你一旦开始watch一个key的时候,开启了一个事务,那么在这个事务提交之前数据发生改变,那么就事务执行失败。从下面的例子就可以看出。

  1. 127.0.0.1:6379[5]> get k
  2. "20"
  3. 127.0.0.1:6379[5]> set k 30
  4. OK
  5. 127.0.0.1:6379[5]>watch k
  6. OK
  7. 127.0.0.1:6379[5]> set k 40
  8. OK
  9. 127.0.0.1:6379[5]> multi
  10. OK
  11. 127.0.0.1:6379[5]> set k 50
  12. QUEUED
  13. 127.0.0.1:6379[5]> exec
  14. (nil)
  15. 127.0.0.1:6379[5]> get k
  16. "40"

java中redis的watch与Transaction功能使用

https://www.liangzl.com/get-article-detail-38409.html
非常好的一个文章。
需求:在项目中遇到了一个使用redis并希望原子性更改值的需要,记录一下。
设置key值,如果传的value比redis中的value大则设置传入的value

  1. /**
  2. * @Description: value greater than get value then set value 设置key值,如果传的value比redis中的value大则设置传入的value
  3. * @param key 需要设置的key
  4. * @param value 需要设置的value
  5. * @return: boolean
  6. * @Author: wangtongxing
  7. * @Date: 2019/1/11
  8. */
  9. public static boolean vGtGetSet(String key, long value) {
  10. String s = null;
  11. Jedis jedis = null;
  12. try {
  13. jedis = JedisPoolUtils.getJedis();
  14. List<Object> exec = null;
  15. int i=0;
  16. while ((exec == null || exec.size()==0 || !"OK".equals(exec.get(0))) && i<5){
  17. jedis.watch(key);
  18. String getS = jedis.get(key);
  19. long get = 0;
  20. if (StringUtils.isNotEmpty(getS)){
  21. try {
  22. get = Long.parseLong(getS);
  23. }catch (Exception e){
  24. e.printStackTrace();
  25. }
  26. }
  27. if (value>get){
  28. Transaction transaction = jedis.multi();//返回一个事务控制对象
  29. transaction.set(key, Long.toString(value));//预先在事务对象中装入要执行的操作
  30. transaction.expire(key, CACHE_6_MONTHS);
  31. exec = transaction.exec();//执行
  32. }else {
  33. jedis.unwatch();
  34. return true;
  35. }
  36. i++;
  37. }
  38. } catch (Exception e) {
  39. e.printStackTrace();
  40. } finally {
  41. JedisPoolUtils.returnRes(jedis);
  42. }
  43. return false;
  44. }

使用中需要注意的几个点:
1.在不成功的情况下,一般需要重试几次,在重试的过程中每次循环都需要重新watch操作,因为每次事务提交之后,watch操作都会失效。
2.在事务提交之后返回的结果对象分为几种情况
事务提交前,watch的key发生改变,返回的List对象并不是null,而是一个初始化后的空对象(size==0)
事务提交前,watch的key没有改变,事务提交成功,返回的List对象中有一个”OK”的String对象。