SQL 入门 16:SQL 事务隔离级别与死锁解析(易懂)

事务隔离级别是数据库并发控制的核心,定义了事务之间如何隔离以及一个事务能看到其他事务更改的程度。本文基于 sql_store 数据库,通过代码示例详细讲解 MySQL 支持的四种标准事务隔离级别及其影响,结合场景说明隔离级别的作用,并解析死锁问题及其规避方法,帮助读者深入理解事务并发管理。

学习内容

1. 事务隔离级别概述

MySQL 支持四种标准事务隔离级别,从低到高分别为:

  • 读未提交(READ UNCOMMITTED)
    最低隔离级别,事务可读取其他事务未提交的更改,导致脏读问题。性能最高,但可能出现脏读、不可重复读和幻读。
  • 读已提交(READ COMMITTED)
    事务只能读取已提交的更改,避免脏读,但仍可能出现不可重复读和幻读。许多数据库(如 Oracle、PostgreSQL)的默认级别。
  • 可重复读(REPEATABLE READ)
    MySQL InnoDB 的默认隔离级别,通过多版本并发控制(MVCC)和间隙锁,确保同一事务内多次读取相同数据结果一致,避免脏读和不可重复读,但可能出现幻读(InnoDB 通过锁机制部分缓解)。
  • 串行化(SERIALIZABLE)
    最高隔离级别,强制事务串行执行,完全避免脏读、不可重复读和幻读,但性能最低。

隔离级别越高,并发性能越低,但数据一致性越高。ATM 转账场景中,合适的隔离级别可确保账户余额更新时不被其他事务干扰。

2. 查看与设置隔离级别

通过 SHOW VARIABLES LIKE 'transaction_isolation' 查看当前隔离级别,默认显示 REPEATABLE-READ。可通过以下语句设置隔离级别:

  • SET TRANSACTION ISOLATION LEVEL <级别>:影响下一个事务。
  • SET SESSION TRANSACTION ISOLATION LEVEL <级别>:影响当前会话。
  • SET GLOBAL TRANSACTION ISOLATION LEVEL <级别>:影响全局新会话。

3. 并发问题与隔离级别

不同隔离级别对脏读、不可重复读和幻读的控制能力不同:

  • 脏读:读取未提交数据,可能导致错误决策。
  • 不可重复读:同一事务内多次查询结果不一致,因其他事务提交了更改。
  • 幻读:同一事务内查询范围行数变化,因其他事务插入或删除行。

4. 死锁

死锁是并发事务中两个或多个事务互相等待对方释放资源,导致无法继续执行。MySQL 会检测死锁并终止一个事务,报错提示。减少死锁需保持一致的资源访问顺序或缩短事务长度。

示例代码与讲解

1. 查看隔离级别

SHOW VARIABLES LIKE 'transaction_isolation';

显示当前隔离级别,默认值为 REPEATABLE-READ,MySQL InnoDB 的标准配置。

2. 设置隔离级别

SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

将当前会话的隔离级别设为 SERIALIZABLE,确保后续事务串行执行,避免所有并发问题。

3. 读未提交(READ UNCOMMITTED)

控制台 1

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

SELECT points FROM customers WHERE customer_id = 1;

控制台 2

START TRANSACTION;

UPDATE customers SET points = 20 WHERE customer_id = 1;-- 未提交

在控制台 1 设置 READ UNCOMMITTED 后,查询客户 1 的积分(原为 2303)。控制台 2 更新积分为 20 但未提交,控制台 1 再次查询将看到 20,体现了脏读。若控制台 2 回滚,控制台 1 的查询结果无效,可能误导业务决策。

4. 读已提交(READ COMMITTED)

控制台 1

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

SELECT points FROM customers WHERE customer_id = 1;

控制台 2

START TRANSACTION;

UPDATE customers SET points = 20 WHERE customer_id = 1;

COMMIT;

在控制台 1 设置 READ COMMITTED 后,查询积分。若控制台 2 未提交更改,控制台 1 仍看到原值 2303,避免脏读。控制台 2 提交后,控制台 1 查询得到 20。但若在同一事务内多次查询,可能因其他事务提交导致结果不一致,产生不可重复读。

控制台 1(不可重复读示例)

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

START TRANSACTION;

SELECT points FROM customers WHERE customer_id = 1;-- 假设返回 20

SELECT points FROM customers WHERE customer_id = 1;-- 返回 30

COMMIT;

控制台 2

START TRANSACTION;

UPDATE customers SET points = 30 WHERE customer_id = 1;

COMMIT;

控制台 1 第一次查询得到 20,控制台 2 提交后更新为 30,控制台 1 第二次查询得到 30,体现不可重复读,数据一致性可能受影响。

5. 可重复读(REPEATABLE READ)

控制台 1

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

START TRANSACTION;

SELECT points FROM customers WHERE customer_id = 1;-- 返回 30

SELECT points FROM customers WHERE customer_id = 1;-- 仍返回 30

COMMIT;

控制台 2

START TRANSACTION;

UPDATE customers SET points = 40 WHERE customer_id = 1;

COMMIT;

在 REPEATABLE READ 下,控制台 1 事务开始时创建一致性视图,基于事务启动时的快照(积分为 30)。控制台 2 提交更改为 40,但控制台 1 第二次查询仍返回 30,避免不可重复读。MVCC 确保事务内数据一致。

幻读示例

控制台 1

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

START TRANSACTION;

SELECT * FROM customers WHERE state = 'CO';-- 返回 1 条记录

COMMIT;

控制台 2

START TRANSACTION;

UPDATE customers SET state = 'CO' WHERE customer_id = 1;

COMMIT;

控制台 1 查询 state = 'CO' 的客户,初始仅 1 条记录。控制台 2 将客户 1 的状态改为 'CO' 并提交。控制台 1 提交事务后再次查询,仍得到 1 条记录,未反映数据库最新状态,产生幻读。InnoDB 的间隙锁可部分缓解幻读。

6. 串行化(SERIALIZABLE)

控制台 1

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

START TRANSACTION;

SELECT * FROM customers WHERE state = 'CO';

COMMIT;

控制台 2

START TRANSACTION;

UPDATE customers SET state = 'CO' WHERE customer_id = 2;

COMMIT;

在 SERIALIZABLE 下,控制台 1 查询 state = 'CO' 的客户(初始 2 条)。控制台 2 尝试将客户 2 状态改为 'CO' 但未提交,控制台 1 事务会被阻塞,等待控制台 2 提交,避免幻读。提交后,控制台 1 查询正确反映最新数据。

7. 死锁

控制台 1

START TRANSACTION;

UPDATE customers SET state = 'VA' WHERE customer_id = 1;

UPDATE orders SET status = 1 WHERE order_id = 1;

COMMIT;

控制台 2

START TRANSACTION;

UPDATE orders SET status = 1 WHERE order_id = 1;

UPDATE customers SET state = 'VA' WHERE customer_id = 1;

COMMIT;

控制台 1 锁定 customers 的行,尝试锁定 orders;控制台 2 锁定 orders,尝试锁定 customers,形成循环依赖,导致死锁。MySQL 检测到死锁后终止一个事务,报错提示。规避死锁需统一资源访问顺序或缩短事务长度。

总结

事务隔离级别决定了并发事务的数据可见性和一致性。READ UNCOMMITTED 性能高但风险大,SERIALIZABLE 安全但性能低,REPEATABLE READ 是 MySQL InnoDB 的默认折中方案。ATM 转账场景说明了隔离级别对数据一致性的重要性。本文基于 sql_store 数据库,通过代码示例解析四种隔离级别及死锁问题。后续内容将探讨索引设计或查询优化,敬请关注。

相关推荐
AI 小老六3 小时前
Claude Code 如何压缩上下文:Microcompact、Prompt Cache 与 cache_edits 工程拆解
数据库·人工智能·ai·语言模型·架构·系统架构
Chasing__Dreams3 小时前
Redis--基础知识点--32--redis底层存储结构
数据库·redis·缓存
不总是3 小时前
[2026最新] Windows 免安装版 MySQL 8 详细安装配置教程(ZIP 压缩包版)
数据库·windows·mysql
tedcloud1234 小时前
DBX部署教程:打造支持AI SQL助手的数据库管理环境
数据库·人工智能·sql
野生技术架构师4 小时前
我有个大胆的想法,用 PostgreSQL 代替 Redis
数据库·redis·postgresql
cfm_29144 小时前
Redis ZSet 有序集合详解
数据库·redis·缓存
瀚高PG实验室4 小时前
V4.5.6.1授予普通用户监控类系统表及视图的查询权限
数据库·瀚高数据库
BullSmall4 小时前
模板库与抽取实例:企业数据同步最佳实践
数据库
云策数链4 小时前
用友U8数据库核心表结构与业务关联解析(附常用查询SQL)
数据库·sql·erp·用友·云策数链