• Redis从入门到放弃(5):事务


    1、事务的定义

    Redis的事务提供了一种“将多个命令打包, 然后一次性、按顺序地执行”的机制。

    redis事务的主要作用就是串联多个命令防止别的命令插队。

    但是,事务并不具有传统数据库事务的特性,如回滚。

    2、事务命令

    Redis中的事务可以通过以下命令来执行:

    • MULTI:用于开启一个事务块,表示之后的命令将被放入事务队列中,但并不会立即执行。
    • 事务命令:在MULTIEXEC之间的所有命令都将被添加到事务队列中。
    • EXEC:用于执行事务队列中的所有命令。执行事务后,事务队列会被清空。
    • DISCARD:用于取消事务,清空事务队列中的命令。
    • WATCH:监视一个或多个键,当被监视的键被修改时,事务将被打断。

    Redis事务分2个阶段:组队/打包阶段、执行阶段

    • 组队/打包阶段:将所有命令加入命令队列
    • 执行阶段:依次执行队列中的命令,期间不会被其他客户端发送的请求命令插队或打断。

    示例:

    redis 127.0.0.1:6379> MULTI
    OK
    redis 127.0.0.1:6379> SET book-name "Redis从入门到放弃"
    QUEUED
    redis 127.0.0.1:6379> GET book-name
    QUEUED
    redis 127.0.0.1:6379> SADD tag "Redis" "入门" "放弃"
    QUEUED
    redis 127.0.0.1:6379> SMEMBERS tag
    QUEUED
    redis 127.0.0.1:6379> EXEC
    1) OK
    2) "Redis从入门到放弃"
    3) (integer) 3
    4) 1) "Redis"
       2) "入门"
       3) "放弃"
    

    3、事务错误处理

    事务错误处理方式分为两个阶段:组队时错误执行时错误

    • 组队时错误:如果在组队过程中出现错误,整个队列中的所有命令都将被取消。

    • 执行时错误:如果在执行阶段出现错误,会导致部分命令执行成功,部分命令执行失败。

    4、事务的冲突问题

    为了解决事务的冲突问题,可以使用锁,包括悲观锁和乐观锁。

    4.1、悲观锁(Pessimistic Lock)

    悲观锁是一种对数据修改持有悲观态度的并发控制方式。它总是假设最坏的情况,每次读取数据时都默认其他线程会更改数据,因此需要加锁操作。

    悲观锁的实现:

    • 传统的关系型数据库使用这种锁机制,比如行锁、表锁、读锁、写锁等,都是在操作之前先上锁。
    • Java 里面的同步 synchronized 关键字的实现。

    4.2、乐观锁(Optimistic Locking)

    乐观锁相对于悲观锁而言,它假设数据一般情况下不会造成冲突,只在数据提交更新时才检测是否冲突。如果冲突,则返回异常信息,让用户决定如何处理。乐观锁适用于读多写少的场景,可以提高程序吞吐量。

    乐观锁的实现:

    • CAS 实现:Java 中java.util.concurrent.atomic包下面的原子变量使用了乐观锁的一种 CAS 实现方式。
    • 版本号控制:一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。当数据被修改时,version 值会 +1。当线程 A 要更新数据时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值与当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。

    Redis通过CAS (Check and Set) 实现乐观锁,使用WATCH指令监听一个或多个键,当用户提交修改事务时,会检查监听的键是否发生变化。若没有发生变化,则提交成功;否则,事务失败。

    例如,两个客户端在同一时间开始一个事务,他们都对同一个键的值进行了修改,但在执行事务的EXEC命令时,只能有一个客户端的事务能够成功,另一个客户端的事务会因为键的值发生了改变而执行失败。

    客户端1:

    # 刷新数据库
    127.0.0.1:6379> FLUSHDB
    OK
    # 设置key值
    127.0.0.1:6379> set key 10
    OK
    # 监听key
    127.0.0.1:6379> WATCH key
    OK
    # 开启事务
    127.0.0.1:6379> MULTI
    OK
    

    客户端2:

    # 监听key
    127.0.0.1:6379> WATCH key
    OK
    # 开启事务
    127.0.0.1:6379> MULTI
    OK
    

    客户端1:

    # 指令加入队列
    127.0.0.1:6379(TX)> INCR key
    QUEUED
    # 执行指令,可以看到执行成功,修改了一条数据,值被更新为11
    127.0.0.1:6379(TX)> EXEC
    1) (integer) 11
    

    客户端2:

    127.0.0.1:6379(TX)> INCR key
    QUEUED
    127.0.0.1:6379(TX)> exec
    (nil)
    

    5、事务三特性

    Redis事务具有以下三个特性:

    1. 单独的隔离操作:事务中的命令会被序列化并按顺序执行,不会被其他客户端的指令打断。
    2. 没有隔离级别的概念:事务提交前所有指令都不会被执行。
    3. 无原子性:事务组队时具有原子性,但在执行阶段出错某段指令,事务过程中的指令仍然会生效。
  • 相关阅读:
    MySQL 关键特性一:插入缓冲、双写缓冲
    (01)ORB-SLAM2源码无死角解析-(50) 局部建图线程→SearchInNeighbors():融合重复地图点
    Android12源码编译报错ninja: build stopped: subcommand failed.解决
    Python3操作SQLite3创建表主键自增长|CRUD基本操作
    《MATLAB科研绘图与学术图表绘制从入门到精通》示例:绘制婴儿性别比例饼图
    css制作瀑布流布局
    用于肺结节分类的常规 EHR 的纵向多模态Transformer集成成像和潜在临床特征
    代码注释有感
    秋招突击——算法打卡——6/4——复习{(状态压缩DP)小国王}——新做:{三数之和、最接近的三数之和}
    控制算法::速度前馈
  • 原文地址:https://www.cnblogs.com/myshare/p/17592689.html