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 数据库,通过代码示例解析四种隔离级别及死锁问题。后续内容将探讨索引设计或查询优化,敬请关注。

相关推荐
ywl47081208713 小时前
Mysql 平衡二叉树、红黑树、B树、B+树区别以及应用场景(五)
数据库·b树·mysql
IvorySQL14 小时前
IvorySQL Agent 探索与实践
数据库·人工智能·postgresql·oracle·ivorysql
SelectDB15 小时前
美团数十 PB 规模 Apache Doris 实践:从统一 OLAP 到 AI-Native 数据基座
大数据·数据库·性能优化
Database_Cool_15 小时前
阿里云RDS主从延迟解决方案_只读实例半同步复制最佳实践
数据库·人工智能
KaMeidebaby16 小时前
卡梅德生物技术快报|小 RNA 适配体合成 + 多方法亲和力表征全流程标准化操作手册
前端·网络·数据库·人工智能·算法
卓怡学长16 小时前
w269基于spring boot + vue 候鸟监测数据管理系统
java·数据库·spring boot·spring·intellij-idea
ClouGence16 小时前
MySQL 到 StarRocks 数据迁移同步:同步方案与实践指南
数据库·mysql
行业研究员16 小时前
当数据生产者变成 Agent,数据库底座如何重构?
大数据·数据库·重构
人工智能培训16 小时前
破解数据发展瓶颈 激活数字经济新动能
大数据·数据库·人工智能·深度学习·神经网络·机器学习·生成对抗网络
1234567890@world17 小时前
知识管理 | 数字化 | APQC
大数据·数据库·人工智能