当前位置:首页 > 技术 > 正文内容

MySQL 事务隔离级别与并发问题深度解析及实战演示

访客 技术 2026年8月14日 1

事务的核心特性(ACID)

关系型数据库中的事务(Transaction)代表了一个逻辑上的最小工作单元。它要求内部包含的所有数据库操作必须作为一个整体被执行,即要么全部生效,要么全部撤销。InnoDB 存储引擎通过以下四大特性(ACID)来保障数据的可靠性:

  • 原子性(Atomicity):事务是不可分割的整体。其底层依赖 Undo Log(回滚日志)来记录数据修改前的状态,一旦事务执行失败或主动回滚,引擎会利用 Undo Log 将数据恢复到初始状态。
  • 一致性(Consistency):事务执行前后,数据库必须从一个完整性状态过渡到另一个完整性状态。这不仅依赖数据库自身的约束(如外键、唯一性),还需要应用程序的业务逻辑来共同保障。
  • 隔离性(Isolation):在并发环境下,多个事务的执行应当互不干扰。InnoDB 主要通过锁机制(如行锁、表锁)和多版本并发控制(MVCC)来实现不同级别的隔离。
  • 持久性(Durability):事务一旦成功提交,其对数据的修改就是永久性的。即使数据库进程崩溃或服务器宕机,Redo Log(重做日志)也能确保已提交的数据在恢复时不会丢失。

并发事务引发的数据异常

当多个事务同时操作相同的数据集时,如果隔离措施不当,可能会引发以下三种典型的并发问题:

  • 脏读(Dirty Read):当前事务读取到了其他事务尚未提交的中间状态数据。若该事务随后发生回滚,当前事务基于这些"脏数据"所做的业务决策将是错误的。
  • 不可重复读(Non-repeatable Read):在同一个事务的生命周期内,多次读取同一行数据却得到了不同的结果。这通常是因为在两次读取之间,该行数据被其他事务修改并提交了。
  • 幻读(Phantom Read):在同一个事务内,多次执行相同条件的范围查询,返回的结果集行数不一致。这通常是因为其他事务在期间插入或删除了符合查询条件的新记录。

事务隔离级别与配置

为了平衡数据一致性与系统并发性能,SQL 标准定义了四种隔离级别。MySQL 默认采用可重复读(Repeatable Read)级别。

事务隔离级别与并发问题矩阵

可以通过以下 SQL 语句查看和动态调整当前会话的隔离级别:

-- 获取当前会话的事务隔离级别
SELECT @@transaction_isolation AS current_isolation_level;

-- 切换会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

隔离级别实战演示

以下演示基于 employee_salary 表(包含 emp_idsalary 字段),通过模拟客户端 A 和客户端 B 的并发交互来验证各隔离级别的行为。

1. 读未提交(Read Uncommitted)

在此级别下,事务可以读取其他事务未提交的修改,无法解决任何并发问题。

-- 客户端 A
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN;
SELECT salary FROM employee_salary WHERE emp_id = 101; 
-- 初始结果: 5000

-- 客户端 B
BEGIN;
UPDATE employee_salary SET salary = 8000 WHERE emp_id = 101;
-- 此时 B 尚未提交事务

-- 客户端 A
SELECT salary FROM employee_salary WHERE emp_id = 101; 
-- 结果: 8000 (发生脏读,读取了 B 未提交的数据)

-- 客户端 B
ROLLBACK; -- B 撤销了修改,A 之前读取的 8000 成为无效的脏数据

2. 读已提交(Read Committed)

此级别解决了脏读,但无法避免不可重复读。其底层原因在于 ReadView 的生成时机:InnoDB 会在每次执行快照读(普通 SELECT)时重新生成一个 ReadView。

-- 客户端 A
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
SELECT salary FROM employee_salary WHERE emp_id = 101; 
-- 初始结果: 5000

-- 客户端 B
BEGIN;
UPDATE employee_salary SET salary = 8000 WHERE emp_id = 101;
COMMIT; -- B 提交了修改

-- 客户端 A
SELECT salary FROM employee_salary WHERE emp_id = 101; 
-- 结果: 8000 (发生不可重复读,因为 A 的第二次查询生成了新的 ReadView,看到了 B 的提交)

3. 可重复读(Repeatable Read)

MySQL 的默认级别。它解决了不可重复读,因为在事务首次执行快照读时生成 ReadView 后,后续的所有快照读都会复用该 ReadView。但在特定场景下仍会引发幻读现象。

-- 客户端 A
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
SELECT * FROM employee_salary WHERE emp_id = 105; 
-- 结果: Empty set (记录不存在)

-- 客户端 B
BEGIN;
INSERT INTO employee_salary (emp_id, salary) VALUES (105, 6000);
COMMIT; -- B 插入并提交了新记录

-- 客户端 A
SELECT * FROM employee_salary WHERE emp_id = 105; 
-- 结果: Empty set (快照读复用 ReadView,依然看不到 B 插入的记录)

INSERT INTO employee_salary (emp_id, salary) VALUES (105, 7000); 
-- 报错: Duplicate entry '105' for key 'PRIMARY'

幻读现象解析:在 RR 级别下,普通的 SELECT 采用快照读,无法看到其他事务新插入的记录。然而,当执行 INSERT、UPDATE 或 DELETE 时,InnoDB 会进行当前读(Current Read)。这就导致了一个看似矛盾的现象:快照读显示记录不存在,但执行插入操作时却因检测到 B 已提交的记录而抛出主键冲突异常。

4. 串行化(Serializable)

最高隔离级别,强制事务串行执行,解决了所有并发问题,但极大地牺牲了并发性能。

-- 客户端 A
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
SELECT * FROM employee_salary WHERE emp_id = 108; 
-- 自动加上共享锁(S锁),转化为当前读

-- 客户端 B
BEGIN;
INSERT INTO employee_salary (emp_id, salary) VALUES (108, 9000); 
-- 尝试获取排他锁(X锁),因与 A 的 S 锁冲突而被阻塞
-- 最终报错: Lock wait timeout exceeded; try restarting transaction

-- 客户端 A
COMMIT; -- A 提交后释放 S 锁,B 的插入操作才得以继续执行

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。