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 数据库,结合代码示例解析事务的核心概念。后续内容将探讨索引设计或查询优化,敬请关注。

相关推荐
oradh8 小时前
Oracle XTTS实现跨版本迁移和升级(Oracle 11g单库升级至19C RAC集群)
数据库·oracle·11g升级19c·xtts跨版本迁移和升级
z123456789869 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
程序猿DD9 小时前
一个 API Key,统一调用大模型、生图和联网搜索
数据库·网关
TlSfoward10 小时前
抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具
数据库·爬虫·网络协议·搜索引擎·php
数智化管理手记10 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
LabVIEW开发10 小时前
NI Package Manager安装LabVIEW时InternalServerError错误
网络·数据库·labview·labview知识·labview功能·labview程序
SelectDB10 小时前
PostgreSQL 实时同步到 Apache Doris:一站式 CDC 解决方案
数据库
欲速不达882010 小时前
Mysql并发DML锁等待的优化
数据库·mysql
Database_Cool_11 小时前
AI Agent 长期记忆怎么存?阿里云 PolarDB-X 对接 Mem0 记忆框架实践
数据库·阿里云
会编程的土豆11 小时前
数据库范式
数据库·oracle