用事务让你的写操作具备原子性,并理解 ACID 保证在日常实践中究竟意味着什么。
在 Kodokon 中打开本课一次银行转账就是两条 UPDATE:从一个账户扣款,给另一个账户入账。如果第二条失败,数据库就在说谎。事务让这个代码块变得不可分割,并带来 ACID 保证:Atomicity 原子性(要么全部完成,要么全都不做)、Consistency 一致性(约束始终保持有效)、Isolation 隔离性(并发的事务永远看不到彼此做到一半的状态)、Durability 持久性(COMMIT 之后即便崩溃也能幸存)。本节课请优先使用真正的 sqlite3 客户端:某些网页沙箱会对每条查询自动提交。
CREATE TABLE accounts (
id INTEGER PRIMARY KEY,
owner TEXT NOT NULL,
balance REAL NOT NULL CHECK (balance >= 0)
);
INSERT INTO accounts (owner, balance)
VALUES
('alice', 500.0),
('bruno', 120.0);正常情况:BEGIN 开启事务,写操作一条条累积起来,COMMIT 把它们一次性地全部定型。在这中间,没有任何其他连接能看到中间状态(alice 已扣款,而 bruno 尚未入账)。
BEGIN;
UPDATE accounts
SET balance = balance - 200
WHERE owner = 'alice';
UPDATE accounts
SET balance = balance + 200
WHERE owner = 'bruno';
COMMIT;
SELECT owner, balance FROM accounts;现在来看失败的情况。给 bruno 扣款 400 会违反 CHECK (balance >= 0):这条语句失败了。SQLite 有个微妙之处:默认情况下,错误只会取消出错的那条语句,但会让事务保持开启。接下来怎么做取决于你的应用代码:ROLLBACK 撤销一切(理智的本能反应),或者在错误可恢复时继续下去。
BEGIN;
UPDATE accounts
SET balance = balance - 400
WHERE owner = 'bruno';
-- Error: CHECK (balance >= 0) rejects the row.
ROLLBACK;
SELECT owner, balance FROM accounts;