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