事务
事务可以一次执行多个命令, 并且带有以下两个重要的保证:
- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
注意:单条命令保证原子性,整个事务操作不保证原子性。
MULTI 、 EXEC 、 DISCARD 事务命令
MULTI 命令用于开启一个事务:
- 它总是返回 OK 。MULTI 执行之后, 客户端可以继续向服务器发送任意多条命令。
- 这些命令不会立即被执行, 而是被放到一个队列中, 当 EXEC命令被调用时, 所有队列中的命令才会被执行
EXEC 命令负责触发并执行事务中的所有命令:
- 如果客户端在使用 MULTI 开启了一个事务之后,却因为断线而没有成功执行 EXEC ,那么事务中的所有命令都不会被执行。
- 另一方面,如果客户端成功在开启事务之后执行 EXEC ,那么事务中的所有命令都会被执行
127.0.0.1:6379> mset k1 v1 k2 v2
QUEUED
127.0.0.1:6379> mget k1 k2
QUEUED
127.0.0.1:6379> exec
1) OK
2) 1) "v1"
2) "v2"
DISCARD 命令的调用 , 客户端可以清空事务队列, 并放弃执行事务.
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> mget k1 k4
1) "v1"
2) (nil)
事务的错误
使用事务时可能会遇上以下两种错误:
- 事务在执行 EXEC 之前,入队的命令可能会出错。比如说,命令可能会产生语法错误(参数数量错误,参数名错误,等等),或者其他更严重的错误,比如内存不足(如果服务器使用 maxmemory 设置了最大内存限制的话)。
- 命令可能在 EXEC 调用之后失败。举个例子,事务中的命令可能处理了错误类型的键,比如将列表命令用在了字符串键上面,
上述两种类似于java种的编译错误与运行时错误。
前者 全部命令失败,后者,除了出错命令失败,其他命令成功,不能保证事务的原子性。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1 k2 v2
QUEUED
127.0.0.1:6379> get k1 k2
(error) ERR wrong number of arguments for 'get' command
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec #失败
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set key1 aaa
QUEUED
127.0.0.1:6379> get key1
QUEUED
127.0.0.1:6379> incr key1
QUEUED
127.0.0.1:6379> exec #成功,但是出错命令失败,
1) OK
2) "aaa"
3) (error) ERR value is not an integer or out of range
redis的乐观锁
watch 监控一个元素,相当于version.
乐观锁成功
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> WATCH money #添加监控
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
执行事务比较moneny的值是否改变,没改变成功。执行成功后,watch自动失效。
乐观锁失败
事务未执行exec操作时。
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
另外一个用户进行修改了money的值
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 100
OK
然后再执行exec操作,失败。
127.0.0.1:6379> exec
(nil)
执行事务比较moneny的值是否改变,改变了失败。
事务失败,watch没有解除监控,要想成功,首先解除监控,然后绑定监控,再进行书屋操作
127.0.0.1:6379> unwatch
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec
1) (integer) 90
2) (integer) 30