当前位置 博文首页 > 文章内容

    【Redis】事务和锁

    作者: 栏目:未分类 时间:2020-07-05 18:01:13

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    纸上得来终觉浅,绝知此事要躬行。

    什么是事务

    事务可以一次执行多个命令,本质是一组命令的集合, 并且带有以下两个重要的保证:

    • 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
    • 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

    为什么要事务

    首先我们先来看一个案例:

    session-1 session-2
    127.0.0.1:6379> set money 1000
    OK
    127.0.0.1:6379> set money 1
    OK
    127.0.0.1:6379> get money
    "1"
    127.0.0.1:6379> get money
    "1"

    上面相当于两个客户端同时进行,session-1为客户端1,session-2为客户端2。客户端1先set money 1000,紧接着客户端2执行set money 1,当客户端1通过get money查看的时候最终的值变成了1,也就是说客户端1在执行的过程中被客户端2插队了,有没有办法保证一个客户端在执行过程中整个操作是一个整体,有,就是下面需要了解的事务以及事务相关的操作命令。

    事务的操作

    MULTI

    • 用于开启一个事务,它总是返回 OK 。 MULTI 执行之后, 客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中。 所有传入的命令都会返回一个内容为 QUEUED 的状态回复(status reply), 这些被入队的命令将在 EXEC 命令被调用时执行。
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> set age 22
    QUEUED
    127.0.0.1:6379> INCR age
    QUEUED
    127.0.0.1:6379> get age
    QUEUED
    127.0.0.1:6379> 
    

    EXEC

    • 命令被调用时, 所有队列中的命令才会被执行。回复是一个数组, 数组中的每个元素都是执行事务中的命令所产生的回复。 其中, 回复元素的先后顺序和命令发送的先后顺序一致。
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> set age 22
    QUEUED
    127.0.0.1:6379> INCR age
    QUEUED
    127.0.0.1:6379> get age
    QUEUED
    127.0.0.1:6379> EXEC
    1) OK
    2) (integer) 23
    3) "23"
    127.0.0.1:6379>
    

    DISCARD

    • 命令被调用时,客户端可以清空事务队列, 并放弃执行事务。之后不能在提交。
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> set age 22
    QUEUED
    127.0.0.1:6379> INCR age
    QUEUED
    127.0.0.1:6379> get age
    QUEUED
    127.0.0.1:6379> DISCARD
    OK
    127.0.0.1:6379> EXEC
    (error) ERR EXEC without MULTI
    127.0.0.1:6379> 
    

    事务工作流程

    事务工作流程

    事务中的错误

    使用事务时可能会遇上以下两种错误:

    • 事务在执行 EXEC 之前,入队的命令可能会出错。比如说,命令可能会产生语法错误(参数数量错误,参数名错误,等等),或者其他更严重的错误。
    127.0.0.1:6379> set name ydongy
    QUEUED
    127.0.0.1:6379> aaa name ydongy2
    (error) ERR unknown command `aaa`, with args beginning with: `name`, `ydongy2`, 
    127.0.0.1:6379> EXEC
    (error) EXECABORT Transaction discarded because of previous errors.
    127.0.0.1:6379> 
    

    我们发现,当事务中出现语法错误,最后exec提示事务不存在,也就是说在事务中执行语法错误,整体事务中所有命令均不会执行。包括那些语法正确的命令

    • 命令可能在 EXEC 调用之后失败。举个例子,事务中的命令可能处理了错误类型的键,比如将列表命令用在了字符串键上面,诸如此类。
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> set name ydongy
    QUEUED
    127.0.0.1:6379> get name
    QUEUED
    127.0.0.1:6379> set age 22
    QUEUED
    127.0.0.1:6379> get age
    QUEUED
    127.0.0.1:6379> lpush name 1 2 3
    QUEUED
    127.0.0.1:6379> get name
    QUEUED
    127.0.0.1:6379> EXEC
    1) OK
    2) "ydongy"
    3) OK
    4) "22"
    5) (error) WRONGTYPE Operation against a key holding the wrong kind of value
    6) "ydongy"
    127.0.0.1:6379> 
    

    上面的案例有一步错误操作,就是把name当成列表进行追加数据,语法本身没有错,但是在最后执行的时候那一条指令并没有执行,但是整个事务中正确的指令都执行了。需要注意已经执行完毕的命令对应的数据不会自动回滚,即 Redis 不支持回滚,需要程序员自己在代码中实现回滚。

    业务场景:

    业务场景

    为了解决这种并发操作带来的问题,Redis 的 WATCH 命令可以为事务提供 check-and-set (CAS)行为,即:乐观锁。被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消。

    session-1 session-2
    127.0.0.1:6379> set age 22
    OK
    127.0.0.1:6379> get age
    "22"
    127.0.0.1:6379> WATCH age
    OK
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> INCR age
    QUEUED
    127.0.0.1:6379> DECR age
    (integer) 21
    127.0.0.1:6379> EXEC
    (nil)
    127.0.0.1:6379> get age
    "21"
    127.0.0.1:6379> get age
    "21"

    通过EXEC 返回nil来表示事务已经失败。当 EXEC 被调用时,不管事务是否成功执行,对所有键的监视都会被取消。另外,当客户端断开连接时,该客户端对键的监视也会被取消。如果需要WATCH 对多个键进行监视,可以使用无参数的 UNWATCH 命令手动取消对所有键的监视。