Redis事务
简介
简介
- Redis事务简单来说,就是一组redis命令的集合,先将这些命令序列化并放到事务的队列中,在发起了
exec执行事务的命令时,这些命令才被执行 - Redis事务没有隔离级别的概念
- Redis单条命令保证原子性,但是Redis事务不保证原子性。
- Redis事务执行后,该事务就消失了,但是事务中命令创建的数据还在~~~
Redis事务的执行过程
- 开启事务 ---
multi - 在事务中放入Redis命令
- 执行事务 ----
exec
Redis事务演示
简单事务
#开启事务
127.0.0.1:6379> multi
OK
#在事务中放入的命令
127.0.0.1:6379(TX)> set k1 1
QUEUED #成功将命令放入事务
127.0.0.1:6379(TX)> set k2 2
QUEUED
127.0.0.1:6379(TX)> set k3 3
QUEUED
127.0.0.1:6379(TX)> get k3
QUEUED
127.0.0.1:6379(TX)> get k1
QUEUED
#执行事务
127.0.0.1:6379(TX)> exec
#执行结果
1) OK
2) OK
3) OK
4) "3"
5) "1"
放弃事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 1
QUEUED
127.0.0.1:6379(TX)> set k2 2
QUEUED
127.0.0.1:6379(TX)> set k3 3
QUEUED
127.0.0.1:6379(TX)> get k3
QUEUED
127.0.0.1:6379(TX)> discard # 放弃事务
OK
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379> get k2
(nil)
127.0.0.1:6379> get k3
(nil)
事务命令编译异常
- 代码问题,所有命令都不会被执行。
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 1
QUEUED
127.0.0.1:6379(TX)> set k2
(error) ERR wrong number of arguments for 'set' command # 放入队列的时候报错,但还是放入了队列,不影响其他命令继续放入队列
127.0.0.1:6379(TX)> get k3
QUEUED
127.0.0.1:6379(TX)> set k3 3
QUEUED
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors. # 执行时提示编译异常
127.0.0.1:6379> get k1 #测试,事务中的命令失效
(nil)
事务命令运行异常
- 语法问题,有问题的命令不被执行,其他命令不受影响
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 a
QUEUED
127.0.0.1:6379(TX)> incr k1 #语法错误,incr自增,k1的值只能是数字
QUEUED
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> set k2 2
QUEUED
127.0.0.1:6379(TX)> set k3 3
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> exec # 执行事务发现只有存在语法错误的命令没有被实现,其他命令照样执行。
1) OK
2) (error) ERR value is not an integer or out of range
3) "a"
4) OK
5) OK
6) "2"
Redis事务乐观锁
- 监控实现
watch - 获取
watch - 更新时比较
watch直接实例演示
成功事务乐观锁
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 #监控money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 50 取50,银行的money少50
QUEUED
127.0.0.1:6379(TX)> incrby out 50 手里现金多50
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 50
2) (integer) 50
失败事务乐观锁
线程一:取20,取完后,还没有执行的时候被线程二插队
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(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> incrby out 20
QUEUED
线程二插队:
127.0.0.1:6379> incrby money 500
(integer) 600
127.0.0.1:6379> decrby out 500
(integer) -500
127.0.0.1:6379> get money
"600"
127.0.0.1:6379> get out
"-500"
这时候,再去线程一执行事务,发现执行失败
127.0.0.1:6379(TX)> exec
(nil)
这时候如果我们要正确读取到money,我们就需要unwatch来解决
127.0.0.1:6379> unwatch # 先解锁
OK
127.0.0.1:6379> watch money # 重新获取值,再次监听
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 100
QUEUED
127.0.0.1:6379(TX)> incrby out 100
QUEUED
127.0.0.1:6379(TX)> exec #执行成功
1) (integer) 500
2) (integer) -400