Redis篇5-redis事务

概述

  • 官方说明:https://redis.io/topics/transactions
  • redis“部分”支持事务(部分回滚)
  • 关键命令
    • MULTI 开始事务
    • EXEC 开始执行事务内命令s
    • DISCARD 取消事务并放弃事务内命令s的执行
    • WATCH 监视一个或多个key,开始乐观锁CAS的事务操作
    • UNWATCH 取消所有key监视
  • 从Redis2.2开始支持用于乐观锁的check-and-set (CAS)

开始使用

multi-exec正常提交

mset k1 v1 k2 v2 k3 v3
multi
set k1 vv1
get k1
set k4 v4 
get k4
exec
keys *

multi-exec异常1-注意

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> set k1 11 12
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR syntax error
127.0.0.1:6379> get k5
"v5"
127.0.0.1:6379> get k1
"vv1"
127.0.0.1:6379> keys *
1) "k5"
2) "k1"
3) "k2"
4) "k3"
5) "k4"

可以看出,multi后加入事务命令OK,但exec执行时出错时,并没有全部回滚。。

multi-exec异常2-说redis“部分支持事务”的原因

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> getset k3
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> getset k3 v3
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> keys *
(empty list or set)

可以看出,exec之前出错,整个事务回滚,即DISCARD。

主动 DISCARD

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> keys *
(empty list or set)

discard比较好理解,就是主动退出/回滚事务,取消命令的执行。

乐观锁-WATCH-CAS

  • 乐观锁一般相对于悲观锁理解,优点是不阻塞,缺点是失败率变高
  • 事务开始前(multi),watch目标key,如果提交前目标key被其他会话改动(即CAS过程),则事务回滚(DISCARD)
  • client1,session1
127.0.0.1:6379> get k1
"vv1"
127.0.0.1:6379> watch k1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 0v1
QUEUED
127.0.0.1:6379> get k1 
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> watch k1
OK
127.0.0.1:6379> multi 
OK
127.0.0.1:6379> set k1 vv1
QUEUED
127.0.0.1:6379> set k2 vv2
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> mget k1 k2
1) "vvvv1"
2) "v2"
  • client2,session2,在session1 watch k1后exec前执行以下
127.0.0.1:6379> get k1
"vv1"
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k1 vvvv1
OK
127.0.0.1:6379> get k1
"vvvv1"

可以看到结果是全部回滚。


关于redis“部分支持事务”

由上面使用可以看出redis事务并不像传统数据库事务那么“纯粹”——出错便全体回滚。
大部分场景都是“部分事务回滚”,表现有点类似spring配置事务传播行为的 RequiredNew

redis事务操作出错是否全部回滚,本身也“看情况”

  • exec之前出错
    multi到exec之间可以看成是批量收集命令的过程,这个过程中出错(没有QUEUED),exec执行就会全体回滚,相当于主动DISCARD,比如命令的语法错误。
  • exec之后出错
    命令全部收集OK了(QUEUED),此时出错则不会全体回滚,比如对键做了错误操作,类似spring配置事务传播行为的 RequiredNew,提交一个算一个。
  • 官方关于为什么不支持传统回滚的原因(说白了就是redis很快,不需要)
    https://redis.io/topics/transactions#why-redis-does-not-support-roll-backs

    Why Redis does not support roll backs?

当然,使用 watch-multi-cas-exec 不一样,这会DISCARD,代表全部回滚。

综上,结合与传统数据库事务特点的对比,redis“部分支持事务”:

  • exec执行与提交
    redis在exec之前,只是先攒着命令,并没有执行过(传统的数据库是执行了,事后通过undo日志来回滚),exec之后就是批量命令顺序执行,执行多少就多少,提交一个算一个。
  • 不保证原子性
    即上面说的,exec之后,“事务”中的命令,如果有一个出错,其后的命令会继续执行,不受影响。exec之前的话,有点像我们进行编程时的操作前校验,“校验”(指加入命令队列)不通过,便不再继续,也就是redis所谓的回滚(DISCARD)。
posted @ 2019-09-10 22:53  summaster  阅读(401)  评论(0编辑  收藏  举报