当前位置: 首页 > article >正文

在 MySQL 的默认事务隔离级别(可重复读,REPEAT READ)下,事务 A 和事务 B 对同一行数据的操作时会产生什么呢?

在 MySQL 的默认事务隔离级别(可重复读,REPEAT READ)下,事务 A 和事务 B 对同一行数据的操作时会产生什么呢?


一、场景还原

时间线事务 A事务 B
1BEGIN;
2SELECT * FROM t WHERE id=1;
3UPDATE t SET col=100 WHERE id=1;
4BEGIN;
5SELECT * FROM t WHERE id=1;
6UPDATE t SET col=200 WHERE id=1;
7COMMIT;ROLLBACK;
8COMMIT;

二、关键分析

1. 事务 A 未提交时
  • 事务 B 的 SELECT 行为

    • 可重复读 隔离级别下,事务 B 的 SELECT 会读取到事务开始时的快照数据(事务 A 的修改不可见)。
    • 如果事务 B 使用 SELECT ... FOR UPDATE,则会尝试获取 行锁,此时会被阻塞,直到事务 A 提交或回滚。
  • 事务 B 的 UPDATE 行为

    • 无论事务 B 的 SELECT 是否使用 FOR UPDATE,执行 UPDATE 时都会尝试获取 行锁
    • 如果事务 A 未提交,事务 B 的 UPDATE 会被阻塞,直到事务 A 释放锁(提交或回滚)。
2. 事务 A 提交后
  • 事务 B 的 UPDATE
    • 如果事务 A 已提交,事务 B 的 UPDATE 将基于 事务 A 提交后的最新数据 进行修改。
    • 示例
      • 事务 A 将 col100 改为 200 并提交。
      • 事务 B 的 UPDATE 会覆盖为 200新值
3. 事务 A 回滚后
  • 事务 B 的 UPDATE
    • 如果事务 A 回滚,事务 B 的 UPDATE 将基于 原始数据 进行修改。
    • 示例
      • 事务 A 的修改被撤销(col 恢复为原值)。
      • 事务 B 的 UPDATE 基于原值修改。

三、可能结果

场景 1:事务 A 提交前,事务 B 尝试更新
事务 A 状态事务 B 结果
未提交事务 B 的 UPDATE 被阻塞,直到事务 A 提交或回滚(或等待锁超时)
提交事务 B 获取锁,基于事务 A 提交后的数据更新,最终提交成功
回滚事务 B 获取锁,基于原始数据更新,最终提交成功
场景 2:事务 A 提交后,事务 B 更新
  • 事务 B 的 UPDATE 总能成功,但可能覆盖事务 A 的修改(需注意 丢失更新 问题)。

四、锁机制详解

锁类型事务 A事务 B
共享锁 (S)SELECT ... LOCK IN SHARE MODE允许其他事务读,但禁止写
排他锁 (X)UPDATE/DELETE禁止其他事务读写
  • 事务 A 的 UPDATE 会持有排他锁,直到事务结束。
  • 事务 B 的 UPDATE 必须等待排他锁释放

五、隔离级别影响

隔离级别事务 B 的 SELECT 结果事务 B 的 UPDATE 行为
读未提交看到事务 A 未提交的修改可能基于脏数据更新
读已提交看不到事务 A 未提交的修改等待锁释放后更新最新数据
可重复读看不到事务 A 未提交的修改等待锁释放后更新最新数据
串行化事务 B 的 SELECT 会直接被阻塞严格串行执行

六、解决方案

1. 避免丢失更新
  • 乐观锁
    UPDATE t SET col = 200, version = version + 1
    WHERE id = 1 AND version = <查询时的版本号>;
    
  • 悲观锁
    SELECT * FROM t WHERE id = 1 FOR UPDATE;
    -- 在事务中锁定行,再执行更新
    
2. 设置锁等待超时
SET innodb_lock_wait_timeout = 30; -- 单位:秒

七、验证实验

步骤 1:开启两个 MySQL 客户端
-- 客户端 A
BEGIN;
SELECT * FROM t WHERE id = 1;
UPDATE t SET col = 100 WHERE id = 1;

-- 客户端 B
BEGIN;
SELECT * FROM t WHERE id = 1; -- 可重复读下看不到 A 的修改
UPDATE t SET col = 200 WHERE id = 1; -- 被阻塞
步骤 2:观察结果
  • 如果客户端 A 提交:
    -- 客户端 A
    COMMIT;
    
    -- 客户端 B 的 UPDATE 自动执行,提交后数据为 200
    
  • 如果客户端 A 回滚:
    -- 客户端 A
    ROLLBACK;
    
    -- 客户端 B 的 UPDATE 基于原始数据修改
    

总结

  • 事务 B 的更新是否成功 取决于事务 A 是否提交:
    • 若事务 A 提交 → 事务 B 基于最新数据更新,提交成功。
    • 若事务 A 回滚 → 事务 B 基于原始数据更新,提交成功。
    • 若事务 A 未提交且未超时 → 事务 B 等待锁释放。
  • 关键风险:事务 B 可能覆盖事务 A 的修改(丢失更新),需通过锁机制或版本控制解决。

http://www.kler.cn/a/575952.html

相关文章:

  • “此电脑”中删除WPS云盘方法(百度网盘通用)
  • C++的基础(类)练习
  • Modbus TCP转Profibus DP协议转换网关赋能玻璃生产企业设备协同运作
  • nginx简单命令启动,关闭等
  • 嵌入式 ARM Linux 系统构成(3):根文件系统(Root File System)
  • 【Java代码审计 | 第十篇】命令执行漏洞成因及防范
  • HTML页面中divborder-bottom不占用整个底边,只占用部分宽度
  • HTML5的新特性有哪些?
  • 【第22节】C++设计模式(行为模式)-Iterator(迭代器)模式
  • 深入解析MySQL MVCC实现机制:从源码到实战演示隔离级别原理
  • RSA的理解运用与Pycharm组装Cryptodome库
  • Java8——Lambda表达式,常见的内置函数式接口
  • python学习笔记-day4(解决实际问题)
  • 如何查看Elastic-Job在Zookeeper中的注册信息
  • 深入解析MySQL备份技术:从逻辑到物理的全面指南
  • 用OpenCV写个视频播放器可还行?(Python版)
  • while-经典面试题实战
  • UI自动化:Python + Selenium4.6+版本 环境搭建
  • 基于置换对称性的模型融合:实现凸盆地单盆地理论
  • [Vue warn]: Failed to mount component: template or render function not defined