周三下午两点,用户刚点击"确认转账",办公室跳闸了。十分钟后电力恢复,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是数据库给你的承诺,但前提是,你得先正确开启这个承诺。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.