操作系统: ubuntu-16.04-x64
redis版本: 4.0.9
redis中的事务是一组命令的集合。事务同命令一样都是redis的最小执行单元。一个事务中的命令要么都执行,要么都不执行。
1.以下是一个事务的例子,它先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED
127.0.0.1:6379> GET book-name
QUEUED
127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED
127.0.0.1:6379> SMEMBERS tag
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
2) "C++"
3) "Programming"
事务错误
如果一个事务中的某个命令出现错误,redis的处理分为以下两种方式:
(1)语法错误。语法错误指命令不存在或者命令参数的个数不对。
127.0.0.1:6379> SET key1 hello
OK
127.0.0.1:6379> SET key2 world
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET key2 world1
QUEUED
127.0.0.1:6379> SET key1
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> MGET key1 key2
1) "hello"
2) "world"
当出现语法错误时,redis会直接返回错误,连语法正确的命令也不会执行。
(2)运行错误。运行错误指在命令执行时出现的错误。
127.0.0.1:6379> SET key1 hello
OK
127.0.0.1:6379> SET key2 world
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET key1 hello1
QUEUED
127.0.0.1:6379> SADD key2 3
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> MGET key1 key2
1) "hello1"
2) "world"
如果redis的事务中出现了部分命令运行错误,其余正确的命令还是会被执行。
redis的事务没有关系数据库的回滚(rollback)功能。因此程序员需要自己实现回滚功能。
2.取消事务
DISCARD
DISCARD用于取消事务,放弃执行事务块内的所有命令,总是返回OK。
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> PING
QUEUED
127.0.0.1:6379> SET greeting "hello"
QUEUED
127.0.0.1:6379> DISCARD
OK
3.带WATCH的事务
设想如下的场景:李四和王五两个人同时购票,李四的账户有200元,王五的账户有500元,票价为100元。
127.0.0.1:6379> SET ticket 1
OK
127.0.0.1:6379> SET lisi 200
OK
127.0.0.1:6379> SET wangwu 500
OK
现在假如李四购票,需要从总票数ticket减1,然后再从李四的账户上扣款100元,这两个命令组成一个事务。
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECR ticket
QUEUED
127.0.0.1:6379> DECRBY lisi 100
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 0
2) (integer) 100
127.0.0.1:6379> MGET ticket lisi
1) "0"
2) "100"
假如在李四执行MULT到EXEC之间,王五已经完成了购票,总票数已经为0,此时再执行李四的购票操作则会出现逻辑错误。
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECR ticket
QUEUED
127.0.0.1:6379> DECRBY lisi 100
QUEUED
127.0.0.1:6379> EXEC
1) (integer) -1
2) (integer) 100
127.0.0.1:6379> MGET ticket lisi
1) "-1"
2) "100"
#在MUTI和EXEC之间王五执行了DECR ticket
我们看到李四的购票后ticket等于-1,这肯定不符合逻辑。为了解决以上问题,redis提供了WATCH命令。
WATCH 命令用于在事务开始之前监视任意数量的键: 当调用 EXEC 命令执行事务时, 如果任意一个被监视的键已经被其他客户端修改了, 那么整个事务不再执行, 直接返回失败。
此时李四的购票操作如下:
127.0.0.1:6379> WATCH ticket
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECR ticket
QUEUED
127.0.0.1:6379> DECRBY lisi 100
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 0
2) (integer) 100
假如在MULTI和EXEC之间,王五来抢票则会导致整个事务失败。
127.0.0.1:6379> WATCH ticket
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECR ticket
QUEUED
127.0.0.1:6379> DECRBY lisi 100
QUEUED
127.0.0.1:6379> EXEC
(nil)
127.0.0.1:6379> MGET ticket lisi
1) "0"
2) "200"
#在MULTI和EXEC之间王五执行了DECR ticket
我们可以看到在ticket被修改后,李四的购票操作实际上没有执行。总票数和李四的总金额都是正常的。
4.取消WATCH
UNWATCH
Unwatch 命令用于取消 WATCH 命令对所有 key 的监视,总是返回OK。
127.0.0.1:6379> WATCH lock lock_times
OK
127.0.0.1:6379> UNWATCH
OK