1.相关指令
Redis 事务本质上是在服务器上搞了⼀个"事务队列"。每次客⼾端在事务中进⾏⼀个操作,都会把命令先发给服务器,放到"事务队列"中(但是并不会⽴即执⾏)⽽是会在真正收到EXEC
命令之后,才真正执⾏队列中的所有操作。
- multi
- 开启事务
java
multi
- exec
- 执行事务
java
exec
- discard
java
discard
2.Redis 事务的特性
Redis 的事务与 MySQL 事务有很大的区别。
- 不保证原子性:Redis 没有"回滚机制",只能做到这些操作"批量执行"。不能做到"⼀个失败就恢复到初始状态"。
- Redis 初始时没有数据
-
不需要隔离性:事务中的所有命令都会按顺序执行(注意是执行,要 exec 后),不会被其他客户端的命令打断(Redis 是单线程执行请求的)。
-
不需要持久性:Redis 是保存在内存的。是否开启持久化要看配置,和事务⽆关。
-
不保证⼀致性:MySQL 的⼀致性体现的是运⾏事务前 和运⾏后,结果都是合理有效的,不会出现中间⾮法状态。而 Redis 事务,有可能出现数据不一致的情况。
- 客户端1:
java
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set key1 200
QUEUED
- 客户端2:
java
127.0.0.1:6379> set key1 100
OK
- 客户端1:
java
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set key1 200
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
127.0.0.1:6379>
- 客户端2:
java
127.0.0.1:6379> set key1 100
OK
127.0.0.1:6379> get key1
"200"
127.0.0.1:6379>
可以看到,在没有提交事务前,如果有其它客户端修改事务中相关的数据,可能让造成其它客户端的更新丢失了。比如上面案例的客户端2,如何解决呢?可以通过搭配 watch 指令来完成。
3.watch 指令
- 当开启事务的时候,如果对 watch 的 key 进⾏修改,就会记录当前 key 的"版本号"。(版本号是个简单的整数,每次修改都会使版本变⼤)
- 在真正提交事务的时候,如果发现当前服务器上的 key 的版本号已经超过了事务开始时的版本号,就会让对应的 key 执⾏失败,但是没有被 watch 监控的 key 还是会被执行。
- 客户端1:
java
127.0.0.1:6379> watch key
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set key 100
QUEUED
127.0.0.1:6379(TX)> set key1 200
QUEUED
127.0.0.1:6379(TX)>
- 客户端2:
java
127.0.0.1:6379> set key 20
OK
127.0.0.1:6379>
- 客户端1:
java
127.0.0.1:6379> watch key
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set key 100
QUEUED
127.0.0.1:6379(TX)> set key1 200
QUEUED
127.0.0.1:6379(TX)> exec
(nil)
127.0.0.1:6379> get key
"20"
127.0.0.1:6379> get key1
"200"
127.0.0.1:6379>
- 客户端2:
java
127.0.0.1:6379> set key 20
OK
127.0.0.1:6379> get key
"20"
127.0.0.1:6379>
注意到,并没有停止执行事务。没有被 watch 监控的 key 还是会被执行。