专业书籍精读 · DDIA · 第 7 章

事务:并发与故障面前,替应用挡下混乱

Designing Data-Intensive Applications · Ch 7 · Martin Kleppmann · 2017

EN →

这一章讲什么?

你给朋友转账:你的余额扣了 100,朋友的还没加上,这一瞬间机房断电了。钱去哪了?DDIA 第 7 章讲的事务(transaction),就是数据库给出的答案——它保证一组操作「要么全部做成,要么当作从没发生」,绝不停在半路。这一章还告诉你:现实里很多数据库嘴上说的「安全」,其实没你以为的那么安全

打个比方

事务像搬家打包:你把一屋子东西装进一个箱子,贴上封条搬走。到了新家,要么整箱完好送到(提交 commit),要么半路出了岔子、原样退回旧家(回滚 abort)——绝不会「桌子到了、椅子丢在半路」。数据库替你把「扣钱 + 加钱」封进同一个箱子,一起成、一起败。

旧世界为什么难

难点不在一个人慢慢改数据,而在好多人同时改。两个人同时抢最后一张票、同时给同一篇维基词条补一句话、同时把账户余额减一笔——如果数据库不管,就会有人的修改被悄悄覆盖,或者读到一半是旧、一半是新的「花账」。加上机器随时可能崩,混乱是常态。

核心机制直觉

数据库怎么让大家不打架?主流招数是发给每个事务一张「此刻的照片」——你这笔操作从头到尾都看着开始那一瞬的世界,别人后来的改动你看不见,也就不会被搅乱(这叫快照 snapshot)。等你说「提交」时,数据库再回头检查一下:我读的东西,中途被别人动过没有?动过就让你重来一次。各看各的照片、提交时再对账——这就是现代数据库并发的骨架。

先说个怪事:两个医生一起请假

医院规定至少留 2 个医生值班。此刻正好 2 人在岗,两人都想请假。他们几乎同时点了「请假」:小李看了一眼「现在有 2 个人呢,我走没事」,小王也看了一眼「有 2 个人,我走没事」——于是两人都走了,值班室一个不剩。各自看着都合规,合起来却闯了祸。这种「你读你的、我读我的,凑一块坏事」的坑(叫写偏斜 write skew),恰恰是很多号称安全的数据库挡不住的——这一章专门教你认出它。

该怎么选

数据库把「安全」做成一个可调的旋钮(叫隔离级别):拧到最松,快但容易出上面那些乱子;拧到最紧(叫可串行化,效果等于「大家排队一个一个来」),最安全但要么慢、要么老让你重试。麻烦的是名字会骗人:有的数据库把旋钮标成「可串行化」,其实只拧到了中间档。所以别只看名字,要看它到底挡住了哪些坑。

一句话记住

事务 = 「要么全成、要么全废」的一个箱子,替你挡住并发打架中途崩溃两种混乱。但「安全」是可调的旋钮、且名字常常虚标——看它真挡住了哪些坑,别信它自称的档位

一个诚实的代价:把旋钮拧到最紧(真正的可串行化),要么让事务排成单队慢下来,要么在高并发下频繁「撞车重来」——安全从来不是免费的。

想进到 ACID、隔离级别、MVCC 快照与两阶段锁的具体机制? → 切到精读版