Redis 的事务和 MySQL 的事务概念上是类似的,都是把一系列操作绑定成一组,让这一组能够批量执行。
但是注意体会 Redis 的事务和 MySQL 事务的区别:
Redis 事务本质上是在服务器上搞了一个“事务队列”。每次客户端在事务中进行一个操作,都会把命令先发给服务器,放到“事务队列”中(但是并不会立即执行),而是会在真正收到 EXEC 命令之后,才真正执行队列中的所有操作。
因此,Redis 的事务的功能相比于 MySQL 来说,是弱化很多的。只能保证事务中的这几个操作是“连续的”,不会被别的客户端“加塞”,仅此而已。
开启一个事务,执行成功返回 OK。
实例
127.0.0.1:6379> MULTI
OK
真正执行事务。
实例
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 1
QUEUED
127.0.0.1:6379> set k2 2
QUEUED
127.0.0.1:6379> set k3 3
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
3) OK
每次添加一个操作,都会提示“QUEUED”,说明命令已经进入客户端的队列了。
真正执行 EXEC 的时候,客户端才会真正把上述操作发送给服务器。
此时就可以获取到上述 key 的值了。
127.0.0.1:6379> get k1
"1"
127.0.0.1:6379> get k2
"2"
127.0.0.1:6379> get k3
"3"
放弃当前事务。此时直接清空事务队列,之前的操作都不会真正执行到。
实例
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 1
QUEUED
127.0.0.1:6379> set k2 2
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379> get k2
(nil)
在执行事务的时候,如果某个事务中修改的值,被别的客户端修改了,此时就容易出现数据不一致的问题。
实例
# 客户端1 先执行
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key 100
QUEUED
# 客户端2 再执行
127.0.0.1:6379> set key 200
OK
# 客户端1 最后执行
127.0.0.1:6379> EXEC
1) OK
此时,key 的值是多少呢??
从输入命令的时间看,是客户端 1 先执行的 set key 100,客户端 2 后执行的 set key 200。
但是从实际的执行时间看,是客户端 2 先执行的,客户端 1 后执行的。
127.0.0.1:6379> get key
"100"
这个时候,其实就容易引起歧义。
因此,即使不保证严格的隔离性,至少也要告诉用户,当前的操作可能存在风险。
watch 命令就是用来解决这个问题的。watch 在该客户端上监控一组具体的 key。
实例
客户端 1 先执行
127.0.0.1:6379> watch k1 # 开始监控 k1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 100 # 进行修改,从服务器获取 k1 的版本号是 0,记录 k1 的版本号。(还没真修改呢,版本号不变)
QUEUED
127.0.0.1:6379> set k2 1000
QUEUED
# 只是入队列,但是不提交事务执行。
客户端 2 再执行
127.0.0.1:6379> set k1 200 # 修改成功,使服务器端的 k1 的版本号 0 -> 1
OK
客户端 1 再执行
127.0.0.1:6379> EXEC # 真正执行修改操作,此时对比版本发现,客户端的 k1 的版本号是 0,服务器上的版本号是 1,版本不一致!说明有其他客户端在事务中间修改了 k1 !!
(nil)
127.0.0.1:6379> get k1
"200"
127.0.0.1:6379> get k2
(nil)
此时说明事务已经被取消了,这次提交的所有命令都没有执行。
取消对 key 的监控,相当于 WATCH 的逆操作,此处不做演示。