• Redis-事务即简单锁应用


    Redis支持简单的事务, Redis允许一组命令在单一步骤中执行, 事务有两个属性

    • 事务是一个单独的隔离操作, 事务中所有的命令都会序列化, 按照顺序执行.
    • Redis事务是原子性的, 即要么都执行, 要么都不执行

    一个事务从开始到执行会经历三个阶段

    • 开始事务
    • 命令入队
    • 执行事务

    redis 与 mysql 事务的对比:

    mysqlredis
    开启start transactionmulti
    语句普通的sql普通命令
    失败rollback回滚discard取消
    成功commitexec
    __注__: rollback 与 discard 的区别 如果已经成功执行了2条语句, 第3条语句出错 rollback后, 前两条语句影响消失 discard只是结束本次事务, 前两条语句造成的影响依然存在 __注__: 在multi后面的语句中, 语句出错可能有2种情况 1. 语法本身有问题, 这种错误exec时报错, 所有语句得不到执行 2. 语法本身没错误, 但适用对象有问题, 比如zadd操作list对象, exec后会执行正确的语句, 并跳过有不适当的语句

    使用redis模拟银行转账操作:

    • 正常情况
    127.0.0.1:6379> set wang 200    #wang有200
    OK
    127.0.0.1:6379> set zhao 700    #zhao有700
    OK
    127.0.0.1:6379> 
    127.0.0.1:6379> multi    #开启事务
    OK
    127.0.0.1:6379> decrby zhao 100    #zhao减100
    QUEUED
    127.0.0.1:6379> incrby wang 100    #wang加100
    QUEUED
    #以上两个QUEUED表示两条语句被放在队列里面, exec前并没有执行
    127.0.0.1:6379> exec    #执行完毕
    1) (integer) 600
    2) (integer) 300
    127.0.0.1:6379> 
    
    • 意外情况
    127.0.0.1:6379> multi    开启事务
    OK
    127.0.0.1:6379> 
    127.0.0.1:6379> decrby zhao 100    #zhao减100
    QUEUED
    127.0.0.1:6379> das    #输入一个错误的命令
    (error) ERR unknown command 'das'
    127.0.0.1:6379> exec    #执行, 提示被忽略
    (error) EXECABORT Transaction discarded because of previous errors.
    127.0.0.1:6379> 
    127.0.0.1:6379> mget zhao wang    #zhao 和 wang的值不变
    1) "600"
    2) "300"
    127.0.0.1:6379> 
    
    • 另一种报错情况
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> 
    127.0.0.1:6379> decrby zhao 100    #zhao减100
    QUEUED
    127.0.0.1:6379> sadd wang pig    #故意把wang当做数组加入一个key, 发现并没有报错, 
    #因为这条语句被存放在队列里, 并没有被执行
    QUEUED
    127.0.0.1:6379> exec    #此时语句才被执行, 所以报错, 但是zhao依然减了100, 说明执行了正确的语句, 跳过了不正取的语句, 影响还在
    1) (integer) 500
    2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
    127.0.0.1:6379> 
    127.0.0.1:6379> mget zhao wang 
    1) "500"
    2) "300"
    127.0.0.1:6379> 
    
    • discard取消
    127.0.0.1:6379> mget wang zhao
    1) "400"
    2) "400"
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> decrby zhao 100
    QUEUED
    127.0.0.1:6379> incrby wang 100
    QUEUED
    127.0.0.1:6379> 
    127.0.0.1:6379> discard    #取消后值没变
    OK
    127.0.0.1:6379> 
    127.0.0.1:6379> mget wang zhao
    1) "400"
    2) "400"
    127.0.0.1:6379> exec
    (error) ERR EXEC without MULTI
    127.0.0.1:6379> 
    
    127.0.0.1:6379> mget zhao wang
    1) "400"
    2) "400"
    127.0.0.1:6379> 
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> decrby zhao 100
    QUEUED
    127.0.0.1:6379> sadd wang pig
    QUEUED
    127.0.0.1:6379> 
    127.0.0.1:6379> exec
    1) (integer) 300
    2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
    127.0.0.1:6379> 
    127.0.0.1:6379> discard    #不能discard
    (error) ERR DISCARD without MULTI
    127.0.0.1:6379> 
    127.0.0.1:6379> mget zhao wang
    1) "300"
    2) "400"
    127.0.0.1:6379> 
    
    
    

    watch key1 key2 ... keyN

    作用: 监听 key1 key2 keyN有没有变化, 如果有变化, 则事务取消

    unwatch(不加key): 取消所有 watch 监听

    场景: 一个人正在买票, ticket-1, money-100, 而票只有一张, 如果在我multi之后, exec之前票被别人买走, 即ticket变为0了, 怎么办?

    127.0.0.1:6379> set ticket 1    #加入只有1张票
    OK
    127.0.0.1:6379> set lisi 300    #lisi有300
    OK
    127.0.0.1:6379> set wang 300    #wang有300
    OK
    127.0.0.1:6379> 
    127.0.0.1:6379> multi    #开启事务
    OK
    127.0.0.1:6379> decr ticket    #票数-1
    QUEUED
    127.0.0.1:6379> decrby lisi 100    #lisi准备买-100
    QUEUED
    127.0.0.1:6379> #此处还没有exec
    

    假如就在exec前票被别人买走, 打开另一个终端

    127.0.0.1:6379> decr ticket    #票-1
    (integer) 0
    127.0.0.1:6379> get ticket    #此时票数为0
    "0"
    127.0.0.1:6379> 
    

    此时提交lisi

    127.0.0.1:6379> exec
    1) (integer) -1    #票变为-1
    2) (integer) 200    #钱-100
    127.0.0.1:6379> 
    

    因此上面的过程不合理


    要解决上面的情况,要采用监视

    127.0.0.1:6379> set ticket 1    #票数为1
    OK
    127.0.0.1:6379> set lisi 200    #lisi钱是100
    OK
    127.0.0.1:6379> set wang 300    #wang是300
    OK
    127.0.0.1:6379> 
    127.0.0.1:6379> watch ticket    #监控ticket有没有变动, 有变动的话则事务取消
    OK
    127.0.0.1:6379> multi    #开启事务
    OK
    127.0.0.1:6379> decr ticket    #ticket-1
    QUEUED
    127.0.0.1:6379> decrby lisi 100    #钱-100
    QUEUED
    127.0.0.1:6379>
    

    在exec前票又被另一个人买走了

    127.0.0.1:6379> decr ticket
    (integer) 0
    127.0.0.1:6379> get ticket
    "0"
    127.0.0.1:6379> 
    

    此时票数为0, lisi提交

    127.0.0.1:6379> 
    127.0.0.1:6379> exec    #失败
    (nil)
    127.0.0.1:6379> 
    127.0.0.1:6379> get lisi    #钱并没有减少
    "200"
    127.0.0.1:6379> 
    
    
    
  • 相关阅读:
    面向复杂应用,Node.js中的IoC容器 -- Rockerjs/core
    一步步学会用docker部署应用(nodejs版)
    nodeEE双写与分布式事务要点一二
    提升node.js中使用redis的性能
    puppeteer实现线上服务器任意区域截图
    Nodejs“实现”Dubbo Provider
    TypeScript入门教程
    node.js与比特币(typescript实现)
    关于首屏时间采集自动化的解决方案
    回顾2017,未来仍需要不停充电
  • 原文地址:https://www.cnblogs.com/qlshine/p/5958504.html
Copyright © 2020-2023  润新知