数据库改到一半断电了

为什么需要

第 1 页的菜谱表里有一列收藏数:番茄炒蛋有 41 人收藏。里另有一张收藏表,每一行写着谁收藏了哪道菜,第 1024 号的收藏记录也是 41 行。收藏数本可以每次去收藏表里数,但菜谱每被打开一次就要数一遍,所以菜谱表里另存一个现成的数,打开时直接读。两处说的是同一件事,必须对得上。

你点一下收藏,数据库要做两步:第 1 步,在收藏表里加一行;第 2 步,把番茄炒蛋的收藏数从 41 改成 42。要是第 1 步做完,第 2 步还没做,机器就断了电呢?重新开机以后,收藏记录有 42 行,收藏数却还是 41。

怎样做

办法是把这两步绑成一组,要么全做,要么全不做,不会停在中间,这叫(transaction)。你平时说的事务,指要办的事情,也有人会想到买卖的交易;这里是数据库的术语。

程序先告诉数据库「开始」,再做这两步。两步都做完,就(commit):两处改动一起生效,数据库并保证它们断电也不丢。提交之前出了错,或者程序决定不做了,就(rollback),撤销这个事务开始以来做过的所有改动;没来得及提交就断了电,重新开机以后,数据库同样把这半截改动作废。第 14 章讲发布新版本时也说回滚,那里指把程序换回上一个版本,与这里不是一回事。

在下图里,让断电发生在第 1 步做完以后,比一比用事务和不用事务时,重新开机后两张卡片对不对得上。

做到一半:两步之间断电,两处还对得上吗

先看这里先选「不用事务」,按「下一步」一直播到重新开机以后,看两张卡片;再换成「用事务」播一遍。

这一次
断电

收藏记录

收藏表里第 1024 号的行数41 行
  1. 第 1 步:收藏表加一行未做
  2. 第 2 步:收藏数加一未做

收藏数

菜谱表里第 1024 号的收藏数41

做到一半的示意图:左边一张卡片是收藏表里第 1024 号的收藏记录有几行,右边一张是菜谱表里它的收藏数,中间是两个步骤和断电的时刻;用事务时,第 1 步之前有「开始」,第 2 步之后有「提交」。每一步的变化写在图下的说明里。

尚未开始,共 3 步

还没开始。按「下一步」或「自动播放」。

四种情况比一比(收藏记录 / 收藏数)
第 1 步做完以后断电不断电
不用事务——
用事务——

收藏记录和收藏数都是为演示虚构的。「断电」指数据库所在的机器在两步之间断电,重新开机以后再看两张卡片。

事务里的几步是一个分不开的整体,这叫(atomicity);原子说的是不可分割,与大小、快慢无关。提交以后改动不丢,就是第 1 页讲的。事务应当做到的几项保证常合称 ,取四项保证的英文首字母,原子性(A)和持久性(D)是其中两项。

代价是什么

代价是等:提交要等改动真正写进硬盘,才回报成功。有的数据库可以设成先回报成功、稍后再写硬盘,这样快一些,但机器出了故障,最近的几笔改动就可能丢掉。

还要分清是哪一种故障。只是数据库程序崩溃时,机器一直开着:它已经交给的数据,通常由操作系统接着写进硬盘;还留在数据库自己内存里的改动,用了更快的设置时就会丢。整台机器断电时,只有已经写进硬盘的才在。上图演示的是后一种。

资料来源

以下资料均于 2026-10-23 访问。

  1. 3.4. TransactionsPostgreSQL 全球开发组(PostgreSQL 18.6 文档),未注明日期
  2. Appendix M. GlossaryPostgreSQL 全球开发组(PostgreSQL 18.6 文档),未注明日期
  3. Atomic Commit In SQLiteSQLite,2026-04-21
  4. InnoDB and the ACID ModelOracle(MySQL 8.4 参考手册),未注明日期
  5. 28.4. Asynchronous CommitPostgreSQL 全球开发组(PostgreSQL 18.6 文档),未注明日期
  6. 19.5. Write Ahead LogPostgreSQL 全球开发组(PostgreSQL 18.6 文档),未注明日期
  7. InnoDB Startup Options and System VariablesOracle(MySQL 8.4 参考手册),未注明日期
  8. Pragma statements supported by SQLiteSQLite,2026-10-04