Redis(四):事务
redis 事务的相关命令
https://www.redis.net.cn/order/3639.html
序号 | 命令及描述 |
---|---|
1 | DISCARD 取消事务,放弃执行事务块内的所有命令。 |
2 | EXEC 执行所有事务块内的命令。 |
3 | MULTI 标记一个事务块的开始。 |
4 | UNWATCH 取消 WATCH 命令对所有 key 的监视。 |
5 | [WATCH key key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 返回值:事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil |
执行情况
Redis不是强一致性
- EXEC:全部执行
- DISCARD:全部放弃
- EXEC前就有ERR:全体连坐
- EXEC后运行ERR:对的执行,错的不执行
全部执行
全部放弃
全体连坐
一个事务ERROR,全部都不执行
类似Java编译异常
冤头债主
对的执行,错的抛出
类似Java运行异常
WATCH与乐观锁
当 Redis 使用 exec 命令执行事务的时候,它首先会去比对被 watch 命令所监控的键值对
- 如果没有发生变化,那么它会执行事务队列中的命令,提交事务
- 如果发生变化,那么它不会执行任何事务中的命令,而去事务回滚。
注:这里的变化指的是与WATCH指令执行时的值相比,而不是说事务不能改变监控的值
无论事务是否回滚,Redis 都会去取消执行事务前的 watch 命令,这个过程如图所示。
例子:
Redis 参考了多线程中使用的 CAS(比较与交换,Compare And Swap)去执行的。在数据高并发环境的操作中,我们把这样的一个机制称为乐观锁。这句话还是比较抽象,也不好理解。
所以先简要论述其操作的过程,当一条线程去执行某些业务逻辑,但是这些业务逻辑操作的数据可能被其他线程共享了,这样会引发多线程中数据不一致的情况。
为了克服这个问题,首先,在线程开始时读取这些多线程共享的数据,并将其保存到当前进程的副本中,我们称为旧值(old value),watch 命令就是这样的一个功能。
然后,开启线程业务逻辑,由 multi 命令提供这一功能。在执行更新前,比较当前线程副本保存的旧值和当前线程共享的值是否一致,如果不一致,那么该数据已经被其他线程操作过,此次更新失败。
为了保持一致,线程就不去更新任何值,而将事务回滚;否则就认为它没有被其他线程操作过,执行对应的业务逻辑,exec 命令就是执行“类似”这样的一个功能。
注意,“类似”这个字眼,因为不完全是,原因是 CAS 原理会产生 ABA 问题。所谓 ABA 问题来自于 CAS 原理的一个设计缺陷,它可能引发 ABA 问题,如表 1 所示。
时间顺序 | 线程1 | 线程2 | 说明 |
---|---|---|---|
T1 | X=A | — | 线程 1 加入监控 X |
T2 | 复杂运算开始 | 修改 X=B | 线程 2 修改 X,此刻为 B |
T3 | 处理简单业务 | — | |
T4 | 修改 X=A | 线程 2 修改 X,此刻又变回 A | |
T5 | 结束线程 2 | 线程 2 结束 | |
T6 | 检测X=A,验证通过,提交事务 | — | CAS 原理检测通过,因为和旧值保持一致 |