💻 第八章 一用就会的数据库
📄 一句话概括

事务让一组操作要么全成、要么全不做——转账这类【半截不能停】的操作,安全绳交给 DBMS。

📝 本节和前面几节的关系

数据能增删改查了,最后一个问题:如果一组操作做到一半出错了怎么办?本节讲事务。

一、半截操作的灾难

转账要动两条记录:A 扣 100、B 加 100——如果只做完 A 就断电或报错,钱就凭空少了 100; 这类操作的特点是:必须成组完成,不允许停在中间; 自己写代码去补救(手动改回)只是权宜——要考虑的意外太多,顾不过来。

二、事务:把一组操作捆成一件事

BEGIN:开始事务,之后的修改进入临时状态; COMMIT:确认提交,修改正式落盘; ROLLBACK:撤销回滚,把这一组修改全部作废——像什么都没发生过。 三条命令把一组操作捆成【一件事】——这就是事务的原子性:要么全成,要么全不做。

三、为什么交给 DBMS

这件事非 DBMS 不可:它掌握着数据文件与日志(log),就算中途断电,重启后也能照日志把没提交的修改收拾干净; 应用层自己做不了这个保证——它连断电那一刻的数据都摸不到; 【⚠️ 2026 现状】分布式系统把事务扩大到多台机器(分布式事务、最终一致),思路仍是【别停在中间】。

四、动手 Demo:转账与回滚

真跑一次成功转账(COMMIT)与失败转账(ROLLBACK),看余额怎么变。

import sqlite3
con = sqlite3.connect(":memory:", isolation_level=None)
con.execute("CREATE TABLE account (name TEXT PRIMARY KEY, balance INTEGER)")
con.executemany("INSERT INTO account VALUES (?, ?)", [("A", 500), ("B", 200)])
con.execute("BEGIN")                       # 转账: A-100, B+100
con.execute("UPDATE account SET balance = balance - 100 WHERE name='A'")
con.execute("UPDATE account SET balance = balance + 100 WHERE name='B'")
con.commit()                               # 提交
print(list(con.execute("SELECT * FROM account")))
con.execute("BEGIN")                       # 再来一次, 但中途出错
con.execute("UPDATE account SET balance = balance - 100 WHERE name='A'")
con.rollback()                             # 回滚, 撤销半截操作
print(list(con.execute("SELECT * FROM account")))

实跑输出:

--------------------------------------------------------------------
事务控制也可以交给 DBMS 处理
--------------------------------------------------------------------

转账要动两条记录: A 减 100、B 加 100。若只做完一半就断电, 账目就乱了。
事务让这两步【要么全成, 要么全不做】:

    初始:
        余额 -> A=500  B=200
    [成功案例] BEGIN; A-100; B+100; COMMIT;
        提交后 -> A=400  B=300
        合计仍是 700: 100 元一分不多一分不少。

    [失败案例] BEGIN; A-100; 中途出错; ROLLBACK;
        A 已扣、B 未加 -> A=300  B=300
        回滚后 -> A=400  B=300
        回滚把半截操作全撤销, 账目回到干净状态。

原子性(要么全成)是事务的核心——DBMS 用日志保证断电后也能收拾干净。

第一次提交后合计仍是 700,第二次回滚后余额回到原值——一分不多一分不少,这是事务要守住的底线。 回滚能撤销【半截操作】,靠的是 DBMS 记的日志——所以这项保证必须由 DBMS 来做。

30 秒实验

  1. 举一个【必须成组完成】的业务动作(转正、下单扣库存),说说半截完成会出什么乱子。
  2. 说出 BEGIN、COMMIT、ROLLBACK 三个命令各自的作用。

常见误区

  1. 以为应用层自己补一补就能替代事务——断电、并发时应用什么也保证不了。
  2. 以为事务只是编程技巧——它是 DBMS 用日志实现的硬保证,是数据库的基本功。
自测三问(点开对答案) 1. 事务的核心性质是什么? —— 原子性——一组操作要么全成、要么全不做。 2. 三个事务命令各做什么? —— BEGIN 开始、COMMIT 提交、ROLLBACK 回滚撤销。 3. 为什么事务必须由 DBMS 负责? —— 它掌数据文件与日志,断电后还能照日志收拾干净。

相关笔记