对 Redis 事务的一点理解

分享关于 Redis 事务的一些心得,以及使用 Spring Data Redis 操作 Redis 事务的方法

Redis 事务的特别之处

Oracle/MySQL 等关系型数据库事务的作用之一是保证并发访问下数据的一致性,Redis 事务的意义则有些不同。

Redis server 是单线程处理来自 client 的指令,所以,所有命令的执行都是原子操作。举一个简单的例子,单 Redis server 的情况下,多个 client 并发地执行 INCR(自增)命令,是不会返回相同结果的。

Redis 事务的特点我总结如下:

  1. Redis 没有线程安全的问题,事务的意义也不在于保证数据的一致性,而是 保证命令的批量顺序执行,且事务执行期间,Redis 不会执行来自 client 的其他请求
  2. 事务执行过程中,如果有命令执行失败,事务不会终止,而是会继续执行剩下的命令,Redis 没有异常回滚

对「Redis 事务命令要么全部执行,要么全部不执行」这句事实的理解:

  • 事务一旦开始执行,就无法中止,也无法回滚
  • 事务中某条语句的执行结果是错误时,redis 继续执行剩下的指令
  • 事务命令全部不执行的几种情况
    • 没有执行 EXEC 命令(事务实际上没有开始)
    • WATCH 的 key 发生改变
    • DISCARD 命令放弃事务

本质上,开启事务后,所有输入的命令都被缓存在一个队列中,一旦调用 EXEC 命令,队列里的指令被一条一条的执行。

Redis 事务的关键 API

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

使用 Spring Data Redis 操作事务

这几天在做项目的时候,用到了 spring-data-redis 提供的 RedisTemplate 来操作 Redis。关于事务操作的时候会有问题,我是这样写的:

1
2
3
4
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 的 execute 方法来实现事务:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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);
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Site built with Hugo, hosted by Firebase.
Theme Stack designed by Jimmy.