周三下午两点,用户刚点击"确认转账",办公室跳闸了。十分钟后电力恢复,A账户扣了100万,B账户却没到账。这100万去哪儿了?

这不是段子,是每一个没搞懂Transaction的程序员都可能踩的坑。今天把ACID和事务生命周期掰开揉碎讲清楚,看完至少少背三个P0事故。

打开网易新闻 查看精彩图片

一、Transaction:要么全成,要么全黄

数据库里的事务(Transaction),本质上是一组操作的"打包票"。转账要扣A账户、加B账户,这两步必须绑死在一起——成了都成,黄了都黄,不存在中间态。

核心原则就一句:All or Nothing。系统崩溃、网络中断、代码报错,随便哪个环节出问题,数据库自动回滚(Rollback),就当这事没发生过。用户看到的要么是"转账成功",要么是"操作失败",绝不会出现"钱扣了但没到账"的薛定谔状态。

典型场景:储蓄账户转活期。两步操作——减储蓄、加活期。如果减完储蓄后网络断了,事务机制会检测到异常,把扣掉的钱原路退回。用户无感知,数据零损失。

二、ACID四根柱子,撑住数据不翻车

MySQL、PostgreSQL这些关系型数据库靠ACID四字诀保证可靠性:

A - Atomicity(原子性):事务是不可分割的原子。像电灯开关,只有开和关,没有"半开"。

C - Consistency(一致性):事务必须把数据库从一个合法状态带到另一个合法状态。主键约束、外键关联、字段类型,一个都不能破。

I - Isolation(隔离性):多个事务并发执行时,互相看不见、摸不着。每个事务都以为自己是系统在跑的独苗。

D - Durability(持久性):一旦Commit成功,数据就焊死在硬盘上。服务器立马断电,重启后数据还在。

这四条是关系型数据库的底线,金融系统、订单系统、库存系统——凡是钱或货流动的场景,缺一条都是定时炸弹。

三、事务的一生:BEGIN → 干活 → 二选一

标准生命周期四个节点:

1. BEGIN TRANSACTION:打卡上班,标记事务起点

2. 执行SQL:INSERT、UPDATE、DELETE轮番上阵。注意,这时候数据通常只在内存或日志里,还没落盘

3. COMMIT:全部成功,正式写盘,事务结束

4. ROLLBACK:任何一步报错,全盘撤销,数据库回到BEGIN之前的状态

代码层面长这样:

START TRANSACTION;

UPDATE TaiKhoan SET SoDu = SoDu - 100000 WHERE ID = 'TK001';

UPDATE TaiKhoan SET SoDu = SoDu + 100000 WHERE ID = 'TK002';

IF (出错) THEN ROLLBACK; ELSE COMMIT; END IF;

逻辑清晰,但坑藏在细节里。比如UPDATE执行完还没COMMIT时,其他事务能不能读到这条"半成品"数据?这就牵扯到隔离级别,读未提交、读已提交、可重复读、串行化——选错了轻则性能崩,重则数据乱。

四、ACID不是万能药

ACID强是强,但代价也高。每次事务都要保证原子性和持久性,意味着大量磁盘IO、锁竞争、日志写入。高并发场景下,死守ACID可能直接把数据库拖垮。

这时候BASE模型登场:基本可用(Basically Available)、软状态(Soft state)、最终一致(Eventually consistent)。放弃强一致性,换取性能和扩展性。典型的就是NoSQL和分布式系统——点赞数、评论数、商品浏览量,差个几百条不影响业务,但支付订单差一分钱就是事故。

选型原则:钱相关的用ACID,流量相关的用BASE。别反着来。

最后说个冷知识:很多"数据库崩溃丢数据"的事故,根因不是硬件故障,是程序员没正确处理事务边界。Commit放错位置、异常没捕获、嵌套事务没理清——代码能跑,但关键时刻掉链子。ACID是数据库给你的承诺,但前提是,你得先正确开启这个承诺。