什么是数据库事务?一文搞懂 ACID 四大特性(一)
数据库事务系列 · 第 1 篇
引言:一次转账引发的数据危机
想象你正在开发银行转账系统的核心模块。用户张三发起一笔1000元的转账给李四,程序逻辑清晰地分为两步:
- 张三账户余额减少1000元。
- 李四账户余额增加1000元。
一切看似完美。然而,就在程序成功执行完第一步、正准备执行第二步时,数据库服务器突然宕机了。
系统重启后,你惊恐地发现:张三的账户确实少了1000元,但李四的账户余额却纹丝不动。
那这1000元凭空消失了。更可怕的是,没有任何错误日志能告诉你发生了什么。这就是没有事务机制的数据库世界——每个操作都是孤立的个体,一次意外就可能导致数据永久性地陷入不一致状态。
为了解决这类问题,数据库引入了**事务(Transaction)**这一核心概念。
一、事务的概念:一组操作的“生死契约”
事务,是一组数据库操作的集合,它是数据库执行的最小逻辑单元。
这个单元有一个不可动摇的铁律:这组操作要么全部成功(提交,COMMIT),要么全部失败(回滚,ROLLBACK)。绝不允许“成功一半”的中间状态存在。
案例聚焦:回到转账案例,在事务的保护下,它变成了一个不可分割的整体。
-- 这是一个完整的事务-- 操作1:张三账户减1000UPDATEaccountSETbalance=balance-1000WHEREname='张三';-- 操作2:李四账户加1000UPDATEaccountSETbalance=balance+1000WHEREname='李四';-- 如果上述两步都成功,则执行 COMMIT(提交),数据永久生效。-- 如果任何一步失败,则执行 ROLLBACK(回滚),两步操作全部撤销。- 正常世界:两条UPDATE都成功,数据库执行
COMMIT。数据更新,皆大欢喜。 - 异常世界:步骤1成功,步骤2失败(如网络超时、账户异常)。事务机制会立即执行
ROLLBACK,将张三的余额恢复到修改前的状态。最终结果是:张三钱没少,李四钱没多,数据依然保持一致。
事务的本质:它为应用程序提供了一种简化的编程模型。程序员只需告诉数据库“要么全做,要么全不做”,而无需在代码中处理复杂的并发、网络、硬件等底层异常。事务是应用层与复杂数据层之间的可靠“契约”。
二、事务的四大特性(ACID):四大护法,缺一不可
正是ACID这四个特性,共同构成了事务的完整承诺。我们用转账案例,逐一剖析每个特性的角色。
1. 原子性(Atomicity)——“全做或全不做”
- 案例聚焦:原子性保证了张三扣钱和李四加钱必须作为一个整体执行。如果李四加钱失败,整个事务会回滚,张三的扣钱操作也会被撤销。你永远不会看到“钱扣了但没到账”这种中间状态。
- 技术基石:原子性由Undo Log(回滚日志)保障。每次修改数据前,数据库都会在Undo Log中记录修改前的旧值。一旦需要回滚,数据库就利用这些旧值将数据恢复原状,仿佛操作从未发生。
2. 一致性(Consistency)——“合法才是王道”
- 案例聚焦:一致性要求,事务执行前后,张三和李四的账户总金额必须保持不变(1000+2000 = 1100+1900)。同时,如果表上有“余额不能为负”的约束,一致性也保证不会出现负余额。
- 关键说明:一致性需要应用层和数据库共同保证。数据库能确保其自身约束(如主键、外键)不被破坏。但像“转账总额不变”这样的业务规则一致性,必须由你的代码(正确地执行两条UPDATE)来保证。如果代码只扣了张三的钱而忘了给李四加钱,数据库的约束依然满足,但业务上已经不一致了。
3. 隔离性(Isolation)——“让并发事务和平共处”
- 案例聚焦:当张三给李四转账的同时,王五也在给李四转账。隔离性确保了这两个并发的事务不会互相干扰。一个事务不会读取到另一个事务尚未提交的中间状态(比如“李四账户加了1000”但事务还未提交)。
- 技术基石:隔离性主要通过锁机制和MVCC(多版本并发控制)实现。数据库提供了四种隔离级别,在数据一致性和并发性能之间做权衡。我们将在本系列第2、3篇中深入剖析,届时你会看到“脏读”、“不可重复读”等现象是如何产生和解决的。
4. 持久性(Durability)——“一旦提交,永不遗忘”
- 案例聚焦:当客户端收到“转账成功”的提示后,哪怕数据库所在机房突然断电,重启后,张三扣1000、李四加1000的最终结果依然必须存在。数据不能因为系统故障而回退。
- 技术基石:持久性由Redo Log(重做日志)保障。数据修改时,会先以顺序写的方式快速写入Redo Log,并立即返回“提交成功”。之后,数据库后台才会慢慢将数据刷新到磁盘。即使刷盘前系统崩溃,重启后也能根据Redo Log重做已提交的事务,确保数据不丢失。
核心总结:Undo Log 保证了“可以撤回来”,Redo Log 保证了“提交了不丢”。原子性、隔离性、持久性(A、I、D)三者,最终共同服务于一致性(C)这个终极目标,确保数据库从一个正确状态转移到另一个正确状态。
三、事务的版本支持:选对引擎是前提
理解了ACID的理论,我们进入实操前的最后一步准备:确认MySQL环境的支持情况。
在MySQL中,并不是所有存储引擎都支持事务。这直接决定了你能否使用BEGIN、COMMIT、ROLLBACK等命令。
- InnoDB(默认引擎):完整支持事务。所有ACID特性及相关的并发控制机制都在InnoDB中实现。使用事务,必须确保表使用InnoDB引擎。
- MyISAM(早期引擎):不支持事务。在其上执行事务命令不会报错,但不会有任何回滚或提交的效果。
你可以通过以下命令查看MySQL支持的引擎及事务支持情况:
SHOWENGINES;重点关注Engine(引擎名)和Transactions(是否支持事务)两列。只有标记为YES的引擎才能使用事务。
因此,创建表时务必指定或确认默认引擎为InnoDB:
CREATETABLEaccount(idINTPRIMARYKEY,nameVARCHAR(20),balanceDECIMAL(10,2))ENGINE=InnoDB;-- 明确指定引擎四、事务的提交方式与实操演示
现在我们具备了所有理论基础和环境前提,开始动手验证。
1. 提交方式
MySQL默认采用自动提交(AUTOCOMMIT=1)。这意味着每条DML语句执行后,都会自动被提交为一个独立的事务。
查看当前提交方式:
SHOWVARIABLESLIKE'autocommit';设置提交方式(在演示中,我们通常通过显式BEGIN来开启事务,它会临时影响提交行为):
-- 关闭自动提交,改为手动SETautocommit=0;-- 或直接使用 BEGIN/START TRANSACTION 显式开启事务2. 核心事务命令速查
| 命令 | 作用 |
|---|---|
BEGIN;/START TRANSACTION; | 开启一个事务。 |
SAVEPOINT 名称; | 在事务内创建保存点(存档)。 |
ROLLBACK TO 名称; | 回滚到指定保存点。 |
ROLLBACK; | 回滚整个事务到开始状态。 |
COMMIT; | 提交事务,使所有更改永久生效。 |
3. 完整实操演示:见证原子性与持久性
为了直观展示,我们使用两个MySQL会话(终端A和终端B)。请先将两个终端的隔离级别临时设置为READ UNCOMMITTED,以便我们在事务未提交时也能看到数据变化,从而完整追踪过程。
SETSESSIONTRANSACTIONISOLATIONLEVELREADUNCOMMITTED;第一阶段:开启事务与分段操作
- 终端A开启事务:
BEGIN;
终端A插入数据:
INSERT INTO account VALUES (1, '张三', 100);终端B查询:
SELECT * FROM account;→ 可以看到“张三”记录。
- 终端A创建保存点:
SAVEPOINT s1; - 终端A插入数据:
INSERT INTO account VALUES (2, '李四', 10000); - 终端B查询:
SELECT * FROM account;→ 可以看到两条记录。
第二阶段:部分回滚(展现原子性的控制力)
- 终端A回滚到保存点:
ROLLBACK TO s1; - 终端B查询:
SELECT * FROM account;→结果只显示“张三”,“李四”记录消失。- 结论:事务可以精确回滚到任意保存点,证明了原子性的“全做或全不做”的控制能力。
- 结论:事务可以精确回滚到任意保存点,证明了原子性的“全做或全不做”的控制能力。
第三阶段:完整回滚 vs 最终提交(对比原子性的终结与持久性的开始)
- 终端A回滚整个事务:
ROLLBACK; - 终端B查询:
SELECT * FROM account;→ 表只剩张三。
(现在,重新开启事务,对比提交的效果)
- 终端A重新开启事务:
BEGIN; - 终端A插入数据:
INSERT INTO account VALUES (1, '张三', 100); - 终端A直接提交:
COMMIT; - 终端B查询:
SELECT * FROM account;→ “张三”记录永久存在。
第四阶段:模拟崩溃(验证持久性)
- 模拟崩溃:强制关闭终端A的连接。
- 终端B再次查询:
SELECT * FROM account;- 最终结果:“张三”记录完好无损。
- 结论:一旦
COMMIT,修改就永久生效,即使客户端崩溃也无法影响,证明了持久性。
五、总结
本文作为MySQL事务系列的第一篇,我们完成了以下学习:
- 事务的概念:一组不可分割的操作集合,要么全成功(COMMIT),要么全失败(ROLLBACK)。
- ACID四大特性:
- 原子性(A):由Undo Log保障。
- 一致性(C):最终目标,需应用层逻辑保证。
- 隔离性(I):由锁和MVCC保障。
- 持久性(D):由Redo Log保障。
- 使用前提:表引擎必须是InnoDB。
- 核心操作:通过实操演示了
BEGIN、COMMIT、ROLLBACK、SAVEPOINT命令,并验证了原子性和持久性。
📌 下篇预告
事务的隔离性是ACID中最精妙也最复杂的部分。多个事务并发执行时,会产生哪些问题?脏读、不可重复读、幻读具体指什么?四种隔离级别又是如何应对它们的?
下一篇,我们将用大量实操演示,深入剖析这些并发问题的本质。敬请期待
*:通过实操演示了BEGIN、COMMIT、ROLLBACK、SAVEPOINT命令,并验证了原子性和持久性。
