SQL 入门 15:SQL 事务:从 ACID 到四种常见的并发问题

事务是数据库操作的核心机制,确保一组 SQL 语句作为一个整体执行,要么全部成功,要么全部撤销。本文基于 sql_store 数据库,结合 ATM 转账场景,深入讲解事务的 ACID 特性、创建方法及并发控制中的常见问题,帮助读者理解如何保证数据一致性和稳定性。

学习内容

1. 事务概述

事务是一组 SQL 语句的集合,旨在完成特定任务,具有不可分割性。事务的四大特性(ACID)确保数据操作的可靠性:

  • 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不执行,不会出现部分执行的状态。
  • 一致性(Consistency):事务执行前后,数据库从一个一致状态转换到另一个一致状态,满足所有约束。
  • 隔离性(Isolation):并发执行的多个事务互不干扰,确保每个事务独立运行。
  • 持久性(Durability):事务一旦提交,其结果永久保存,即使系统故障也不会丢失。

以 ATM 转账为例:从一个账户扣除 100 元并向另一个账户增加 100 元,这两个操作必须同时成功或同时失败,否则可能导致资金丢失或数据不一致。

2. 创建事务

事务通过 START TRANSACTION 开始,COMMIT 提交确认,ROLLBACK 撤销操作。自动生成的 ID 可通过 LAST_INSERT_ID() 获取,该函数返回当前会话最近一次插入操作的自动递增 ID,适用于跨表关联。

3. 并发与锁定

并发事务运行时,MySQL 使用行级锁保护数据。默认隔离级别为 REPEATABLE READ,写操作(如 UPDATE)会对目标行加排他锁(X 锁),阻止其他事务修改相同行。未释放锁的查询会被阻塞,默认超时时间为 50 秒(由 innodb_lock_wait_timeout 参数控制),超时后报错。

4. 并发常见问题

并发执行可能引发以下问题:

  • 丢失更新:两个事务同时修改同一数据,后提交的事务覆盖前者的修改,导致前者更新丢失。
  • 脏读:一个事务读取到另一个未提交事务的修改,若后者回滚,前者读取的数据无效。常见于 READ UNCOMMITTED 隔离级别。
  • 不可重复读:同一事务内两次查询同一数据,结果不同,因其他事务修改了数据。
  • 幻读:同一事务内两次查询同一范围,结果行数变化,因其他事务插入或删除行。

事务隔离机制(如 REPEATABLE READ)通过锁和版本控制有效缓解这些问题。

示例代码与讲解

1. 创建事务

START TRANSACTION;

INSERT INTO orders (customer_id, order_date, status)

VALUES (1, '2019-01-01', 1);

INSERT INTO order_items

VALUES (LAST_INSERT_ID(), 1, 1, 1);

COMMIT;

此代码开启一个事务,插入一条订单记录及其关联的订单项。LAST_INSERT_ID() 获取刚插入的订单 ID,确保订单与订单项正确关联。COMMIT 提交后,数据正式写入数据库。若单独执行 INSERT 语句而不提交,操作不会生效,体现事务的原子性。

2. 并发与锁定

START TRANSACTION;

UPDATE customers

SET points = points + 10

WHERE customer_id = 1;

COMMIT;

此事务为客户 ID 为 1 的记录增加 10 积分。执行期间,MySQL 对该行加排他锁,阻止其他事务修改。若另一会话尝试更新同一行,会被阻塞,直至锁释放。默认超时 50 秒后,若仍未获得锁,将报错:ERROR 1205: Lock wait timeout exceeded。这是 REPEATABLE READ 隔离级别确保一致性的体现。

3. 并发问题场景

  • 丢失更新
    假设账户余额为 1000 元:
    • 事务 A 读取余额 1000,计算取款 200 后为 800,尚未提交。
    • 事务 B 读取余额 1000,计算存款 500 后为 1500,并提交。
    • 事务 A 提交 800,覆盖事务 B 的 1500,导致 500 元丢失。
      MySQL 的锁机制通常能避免此类问题。
  • 脏读
    事务 A 扣款 100 元但未提交,事务 B 读取到减少后的余额。事务 A 回滚后,事务 B 读取的数据无效。READ UNCOMMITTED 隔离级别可能引发此问题。
  • 不可重复读
    事务 A 两次查询余额,中间事务 B 修改余额,导致两次结果不一致。REPEATABLE READ 通过锁机制防止此问题。
  • 幻读
    事务 A 两次查询某范围订单,第一次返回 10 条,第二次返回 11 条,因事务 B 插入新订单。幻读涉及行增删,较不可重复读更复杂。

总结

事务通过 ACID 特性确保数据库操作的可靠性和一致性。ATM 转账场景生动说明了事务的必要性:扣款与存款必须同步完成。并发控制通过锁和隔离级别有效管理丢失更新、脏读、不可重复读和幻读等问题。本文基于 sql_store 数据库,结合代码示例解析事务的核心概念。后续内容将探讨索引设计或查询优化,敬请关注。

相关推荐
这个DBA有点耶5 小时前
2026年分布式数据库有哪些主流选择?先评估这5个维度再决定
数据库·分布式·dba
这个DBA有点耶7 小时前
从MySQL 5.7到8.0:JSON查询性能差在哪?虚拟列索引vs多值索引怎么选
数据库·mysql·架构
胡写代码7 小时前
数据库审计字段,别再每张表各写一套了——我统一成这 6 个字段
数据库
SelectDB11 小时前
为什么 JSON 正在成为分析数据库新的竞争点?
数据库·json·agent
ClouGence12 小时前
开源数据库管理工具 CloudDM 4.2.0 发布,新增 GoldenDB、KingbaseES 等数据源
数据库·dba·devops
发霉的馒头12 小时前
ORA-00845: MEMORY_TARGET not supported on this system的解决方法
数据库
草莓熊Lotso15 小时前
【Redis 初阶】Set 类型深度解析:去重集合的运算能力与实战场景
linux·网络·数据库·windows·redis·tcp/ip·缓存
煎饼皮皮侠16 小时前
【设计】设计一个web版的数据库管理平台后端(之五) --借鉴mybatis
数据库·mybatis
东风破_1 天前
danci 2:创建的单词书到底存在哪里?从 Supabase 一路理解 ORM、Drizzle 和 RLS
数据库·后端·node.js
九皇叔叔1 天前
《MySQL 体系架构详解:Server 层、SQL 执行流程与 InnoDB、MyISAM、MEMORY 存储引擎》
sql·mysql·架构