zoukankan      html  css  js  c++  java
  • 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)。
  • 相关阅读:
    HUST-1350 Trie
    hihocoder-第六十一周 Combination Lock
    hihocoder-1196 : 高斯消元·二
    hihocoder-1195 : 高斯消元·一
    SPOJ
    HDU-5074
    UVALive
    POJ-2195
    UVALive
    POJ-1556
  • 原文地址:https://www.cnblogs.com/noodlerkun/p/11503655.html
Copyright © 2011-2022 走看看