java 使用RedisTemplate实现Redis事务

关系型数据库事务的作用是保证并发访问下数据的一致性,Redis 事务有些不同,由于 Redis 是单线程的处理来自 client 的指令,所以 Redis 所有命令的执行都是原子性的,举一个简单的例子,单个 Redis 服务器下,并发地执行 INCR 命令,也不会返回相同的结果。

    所以 Redis 事务的意义在于保证命令的批量顺序执行,并且事务执行期间,Redis 不会执行来自 client 的其他请求。有一点需要注意的是,。如果有命令执行失败,还是会继续执行剩下的命令,因为 Redis 没有异常回滚。

    对“Redis 事务命令要么全部执行,要么全部不执行”这句事实的理解:如果有命令执行失败,并不是中断事务,而是继续执行剩下的指令,因为 Redis 不支持异常回滚。全部不执行的情况有 1. 没有执行 EXEC 命令 2.WATCH 的 key 发生改变 3.DISCARD 命令放弃事务。本质上开启事务后,所有输入的命令都被缓存在一个队列中,一旦 EXEC,队列里的指令被一条一条的执行。

关于事务的 API

  • MULTI    开启事务
  • EXEC    执行任务队列里所有命令,并结束事务
  • DISCARD     放弃事务,清空任务队列,全部不执行,并 UNWATCH
  • WATCH key [key1]    MULTI 执行之前,指定监控某 key,如果 key 发生修改,放弃整个事务执行
  • UNWATCH    手动取消监控

    Spring Data Redis 事务问题

    RedisTemplate 来操作 Redis,关于事务操作的时候会有问题:

    redisTemplate.multi();
    redisTemplate.opsForValue().increment("xxx",1);
    redisTemplate.opsForValue().increment("ttt",1);
    redisTemplate.exec();

     


    调用会报一个错误“No ongoing transaction. Did you forget to call multi? ”查了下,RedisTemplate 操作事务不能理所当然地像原生 API 那么写,其实 RedisTemplate 的事务需要自己实现一个 SessionCallBack 来做事务,所以要这么写

    SessionCallback sessionCallback = new SessionCallback() {
        @Override
        public Object execute(RedisOperations redisOperations) throws DataAccessException {redisOperations.multi();
            // TODO: 2017/11/20 命令 1
            // TODO: 2017/11/20 命令 2
            // TODO: 2017/11/20 命令 3
            return redisOperations.exec();}
    };
    

    redisTemplate.execute(sessionCallback);