创建时间: 2026-09-01最后更新: 2026-09-01

1. 事务

很多业务操作不只包含一条 SQL。创建项目时,我们可能还要把创建者加入成员表,并写入第一条任务。如果三步执行到第二步失败,数据库不应该留下一个只有一半数据的项目。

事务把一组 SQL 组成一个不可分割的工作单元。下面的示例在同一个 psql 会话中创建项目、所有者成员关系和第一条任务:

create-project-transaction.psql
01
BEGIN;
02
03
SELECT id AS owner_id
04
FROM users
05
WHERE email = 'xiaoming@example.com'
06
\gset
07
08
INSERT INTO projects (owner_id, name)
09
VALUES (:owner_id, '数据库学习')
10
RETURNING id AS project_id
11
\gset
12
13
INSERT INTO project_members (project_id, user_id, role)
14
VALUES (:project_id, :owner_id, 'admin');
15
16
INSERT INTO tasks (project_id, task_number, title)
17
VALUES (:project_id, 1, '完成数据库章节')
18
RETURNING id AS task_id
19
\gset
20
21
COMMIT;

BEGIN 开始事务,COMMIT 提交整组修改。这里的 \gset 是 psql 元命令,不是 SQL:它要求查询返回一行,并把 owner_id、project_id、task_id 这些列保存成同名变量,后续再通过 :变量名 引用。这样就不会假设新项目的 ID 恰好是某个固定数字。

在 FastAPI 应用中不会使用 \gset。数据库驱动会取得 RETURNING 的结果,再把 ID 作为参数传给后续 SQL,但这些语句仍然必须使用同一条数据库连接。一个连接上的 BEGIN 不能控制连接池中另一条连接执行的语句。

如果中途发现问题,可以执行:

rollback.sql
1
ROLLBACK;

回滚后,这个事务中尚未提交的表数据修改都会撤销。identity 列背后的序列值通常不会随事务回滚,因此回滚后出现 ID 跳号属于正常现象,不能把编号连续性当作业务规则。

事务解决的是多条语句的整体一致性,不是简单的错误捕获语法。事务一旦提交,后续再执行 ROLLBACK 也不能撤销已经提交的数据。

在 psql 默认开启自动提交的模式下,没有显式写 BEGIN 的每条语句也会在自己的事务中执行,成功后立即提交。单条语句本身具有原子性,但三条独立语句仍然是三个事务,不能保证一起成功。不同数据库驱动的默认设置可能不同,进入 Psycopg 后会再说明连接的提交和回滚方式。

2. ACID

事务的核心性质常用 ACID 概括:

性质通俗理解
原子性 Atomicity一组操作要么全部成功,要么全部失败
一致性 Consistency提交后的数据继续满足约束和业务规则
隔离性 Isolation按隔离级别控制并发事务之间的数据可见性和冲突
持久性 Durability提交成功的数据在故障后仍应能够恢复

一致性并不是数据库自动理解所有业务。只有我们正确设计了约束、事务边界和业务检查,数据库才有规则可以维护。

隔离性也不是所有事务永远像串行执行一样。不同隔离级别在一致性与并发能力之间有不同取舍,下一篇会专门分析。

持久性表示提交确认与数据库的恢复机制相关,但生产可靠性还依赖磁盘、备份、复制和正确运维。事务不能代替备份。

正在验证登录状态
请稍候,验证完成后将继续显示文章内容