• Mysql之锁与事务


    mysql中锁与事务的神秘面纱,主要内容包括: 

    1. 共享锁和排它锁的区别以及适合范围

    2. mysql的表锁和行锁的区别

    3. 怎么判断一个sql是否执行了锁,执行的是表锁还是行锁

    4. 事务是什么,怎么用

    5. 事务的特性ACID

    6. 事务的隔离级别 (RU, RC, RR, SER)

    7. 如何查看mysql使用的隔离级别

    目录

    一.锁

    1. 共享锁和排它锁

    a. 共享锁

    b. 排它锁

    c. gapLock 和 next key lock

    2. 表锁和行锁

    3. 如何使用锁

    a. select分析

    b. sql实例分析 

    二.事务

    1. 定义

    2. ACID特性

    3. 隔离级别

    a. 基本概念

    b. RU: Read Uncommited 未提交读

    c. RC: Read Commited 提交读

    d. RR: Repeatable Read 可重复度 

    e. Serializable 可串行化

    f. 常用命令

    4. 使用姿势

    a. 读锁的影响

    b. 写锁的影响

    c. 小结 

    三. 小结

    1. sql分析

    2. 事务


    一.锁

    乐观锁悲观锁,和DB中常说的共享锁独占锁

    • 乐观锁:多线程中的CAS就是一种乐观锁,实际上不加锁,先尝试去执行,如果失败则重试(或者根据失败策略进行处理)

    • 悲观锁:上锁,一次只能有一个线程访问,其他的都只能等待

    1. 共享锁和排它锁

    a. 共享锁

    突出在共享这个关键词上,顾名思义,表示这个锁可以多人共享,一般又可以称为读锁(S锁)

    在DB中,读锁表示所有的读取数据的小伙伴都不会被锁阻塞,可以放心大胆的获取数据,专业一点的说法就是同一时刻,允许多个连接并发的读取同一资源

    b. 排它锁

    排它,表示当某个人持有这个锁之后,其他的人再来竞争锁就会失败,只能等待锁释放, 又称为写锁(X锁)

    在DB中,写锁表示同一时刻,只能有一个小伙伴操作,其他的不管是读还是写,都得排队,专业说法是写锁会阻塞其他的读锁或写锁请求,确保同一时刻只能有一个连接可以写入资源,并防止其他连接读取或者写资源

    c. gapLock 和 next key lock

    • next key lock 主要是范围匹配的场景下,会锁某一个范围区间

    • gapLock 主要用来锁边界

    如下面的case(说明,columnA是非唯一索引,RR隔离级别)

    • where columnA between 10 and 30, next key lock 确保不会在10, 30 之内插入新的数据行

    • where columnA = 10, gap lock 确保不会再次插入一个columnA=10的行

    2. 表锁和行锁

    对于DB的操作,通常会出现两种情况,一个是锁表,一个锁行

    • 表锁:表示整个表被某一个连接占用了写锁,导致其他连接的读锁或者写锁都会阻塞;影响整个表的读写

    • 行锁:表示表中某些行被某个连接占用了写锁,但是其他行,依然可以被其他的连接请求读锁、写锁;仅影响被锁的那些行数据

    那么一个问题就来了,什么sql会导致行锁,什么会导致写锁?甚至我们如何判断一个sql是否会请求锁,请求的是读锁还是写锁呢?

    3. 如何使用锁

    上面一节抛出了问题,那么现在就是来看下如何使用和分析锁了,首先我们是我们最常见的几个sql

    • select

    • update

    • delete

    • insert

    其中很容易得出的结论是 update, delete, insert 三个涉及到写锁;而且这种操作绝大部分的场景是操作具体的某些行(想想为什么?),所以更常见的是行锁

    select读操作则有点特殊

    a. select分析

    MVCC(multiple-version-concurrency-control)是个行级锁的变种,它在普通读情况下避免了加锁操作,因此开销更低。即下面这个没有读锁也没有写锁

    b. sql实例分析 

    MVCC与GAP锁

    Mysql之锁与事务知识要点小结

    SpringBoot 系列教程之事务隔离级别知识点小结

    Spring事务传播属性

    Spring事务传播属性有那么难吗?看这一篇就够了 - 不学无数的程序员的个人空间 - OSCHINA - 中文开源技术交流社区

    Spring事务传播属性-Java例子详解

    Spring-事务的传播属性(六)_piaoslowly的博客-CSDN博客_事务的传播属性

    Spring编程式事务使用

    SpringBoot 系列教程之编程式事务使用姿势介绍篇

    Spring 官方推荐的 @Transactional 还能导致生产事故?

    二.事务

    1. 定义

    事务就是一组原子性的sql,或者说一个独立的工作单元

    事务就是说,要么mysql引擎会全部执行这一组sql语句,要么全部都不执行(比如其中一条语句失败的话)。

    2. ACID特性

    a. A:atomiciy 原子性

    一个事务必须保证其中的操作要么全部执行,要么全部回滚,不可能存在只执行了一部分这种情况出现。

    b. C:consistency一致性

    数据必须保证从一种一致性的状态转换为另一种一致性状态。

    c. I:isolation 隔离性

    在一个事务未执行完毕时,通常会保证其他Session 无法看到这个事务的执行结果

    d. D:durability 持久性

    事务一旦commit,则数据就会保存下来,即使提交完之后系统崩溃,数据也不会丢失

    3. 隔离级别

    前面在分析锁的sql时,就提到了隔离级别,通常有四种: RU, RC, RR, Serializable

    a. 基本概念

    • 脏读:读取到一个事务未提交的数据,因为这个事务最终无法保证一定执行成功,那么读取到的数据就无法保证一定准确

    • 不可重复读:简单来说就是在一个事务中读取的数据可能产生变化,同样的sql,在一个事务中执行多次,可能得到不同的结果

    • 幻读:会话T1事务中执行一次查询,然后会话T2新插入一行记录,这行记录恰好可以满足T1所使用的查询的条件。然后T1又使用相同 的查询再次对表进行检索,但是此时却看到了事务T2刚才插入的新行

    • 加锁读:select * from table ... 的执行是否加了读锁 (这个可以参考上面的sql加锁分析)

    b. RU: Read Uncommited 未提交读

    事务中的修改,即使没有提交,对其他会话也是可见的,即表示可能出现脏读,一般数据库都不采用这种方案

    c. RC: Read Commited 提交读

    这个隔离级别保证了一个事务如果没有完全成功(commit执行完),事务中的操作对其他会话是不可见的,避免了脏读的可能

    但是可能出现不可重复度的情况,举例说明:

    • 会话T1, 执行查询 select * from where id=1,第一次返回一个结果

    • 会话T2, 执行修改 update table set updated=xxx where id=1 并提交

    • 会话T1,再次执行查询 select * from where id=1,这次返回的结果中update字段就和前面的不一样

    实际的生产环境中,这个级别用的比较多,特意查了下公司的db隔离级别就是这个

    一个RC级别的演示过程:

    • 会话1,开启事务,查询

    • 会话2,开启事务,更新DB,提交事务

    • 会话1,再次查询,提交事务

    • 从下面的实际演示结果可以知道,会话1,同一个sql,两次执行的结果不同

    d. RR: Repeatable Read 可重复度 

    一个事务中多次执行统一读SQL,返回结果一样。 这个隔离级别解决了脏读的问题,幻读问题

    实例演示解决脏读的过程(将上面的过程同样来一次)

    • 发现不管会话1同一个sql,返回的结果都是相同的

    e. Serializable 可串行化

    串行事务隔离级别,所有的事务串行执行,最强的隔离级别,通过给事务中每次读取的行加锁,写加写锁,保证不产生幻读问题,但是会导致大量超时以及锁争用问题。

    f. 常用命令

    • 查看当前会话隔离级别: select @@tx_isolation

    • 查看系统当前隔离级别: select @@global.tx_isolation

    • 设置当前会话隔离级别: set session transaction isolation level read committed;

    • 设置系统当前隔离级别: set global transaction isolation level read committed;

    • 命令行,

      • 开始事务: start transactioin;

      • 提交: commit;

    4. 使用姿势

    前面演示事务隔离级别的时候,给出的实例就演示了事务的使用姿势,一般作为三步骤:

    • 开始事务 start transaction;

    • 执行你的业务sql

    • 提交事务 commit;

    我们现在演示以下一个事务中,读锁、写锁对另一个事务的影响

    a. 读锁的影响

    我们采用mysql默认的RR级别进行测试,userId为主键

    b. 写锁的影响

    c. 小结 

    • 读锁,会阻塞其他请求写锁的sql执行

    • 写锁,会阻塞其他读锁和写锁的sql执行

    • 事务只有在提交之后,才会释放锁

    • 额外注意,上面事务在提交之后才会释放锁,因此如果两个事务循环依赖锁时,可能发生死锁

    三. 小结

    1. sql分析

    • select * from table where xxx; (读快照,一般不加锁)

    • select * from table where xxx lock in share mode; (读锁会阻塞其他的写锁请求,但其他的读锁请求没有影响

    • select * from table where xxx for update; (写锁,会阻塞其他的读写请求)

    • update tableName set xxx (写锁)

    • insert (写锁)

    • delete (写锁)

    2. 事务

    简单来讲,事务就是一组sql,要么全部执行成功,要么全部失败

    四个特性: A(原子性)C(一致性)I(隔离性)D (持久性)

    四种隔离级别:(mysql 默认采用的是RR级别)

    更新丢失

    简单来讲,两个事务 A,B 分别更新一条记录的 filedA, filedB 字段,其中事务 B 异常,导致回滚,将这条记录的恢复为修改之前的状态,导致事务 A 的修改丢失了,这就是更新丢失

    脏读

    读取到另外一个事务未提交的修改,所以当另外一个事务是失败导致回滚的时候,这个读取的数据其实是不准确的,这就是脏读

    不可重复读

    简单来讲,就是一个事务内,多次查询同一个数据,返回的结果居然不一样,这就是不可重复度(重复读取的结果不一样)

    幻读

    同样是多次查询,但是后面查询时,发现多了或者少了一些记录

    比如:查询 id 在[1,10]之间的记录,第一次返回了 1,2,3 三条记录;但是另外一个事务新增了一个 id 为 4 的记录,导致再次查询时,返回了 1,2,3,4 四条记录,第二次查询时多了一条记录,这就是幻读

    幻读和不可重复读的主要区别在于:

    • 幻读针对的是查询结果为多个的场景,出现了数据的增加 or 减少

    • 不可重复读对的是某些特定的记录,这些记录的数据与之前不一致

  • 相关阅读:
    群面经验笔记本(持续更新ing...)
    【云原生 | Kubernetes 系列】K8s 实战 如何给应用注入数据
    PhpStudy 2016搭建-DVWA靶场
    AutoJsPro今日头条极速版源码
    章节六:带参数请求数据
    MobileViT v2导出onnx模型时遇Col2Im算子无法导出问题
    C#判断语句
    使用了lombok后如何生成正确源码包
    MediaPlayer
    计算机论文从撰写到选刊,letpub看几区,论文开源和不开源,论文检索网站有哪些
  • 原文地址:https://blog.csdn.net/IT__LS/article/details/126013025