事务
事务用于保证多条 SQL 操作要么全部成功,要么全部回滚。涉及余额变更、订单创建、库存扣减、批量导入等场景时,应使用事务保护数据一致性。
基础用法
begin;
insert into public.orders (user_id, status)
values ('user-1', 'pending');
insert into public.order_logs (user_id, action)
values ('user-1', 'create_order');
commit;
如果中间步骤失败,应执行 rollback。
begin;
update public.accounts
set balance = balance - 100
where id = 1;
update public.accounts
set balance = balance + 100
where id = 2;
rollback;
Node.js 中使用事务
事务必须使用同一个数据库连接执行,不能在连接池上分别执行每条语句。
const client = await pool.connect();
try {
await client.query("begin");
await client.query("update accounts set balance = balance - $1 where id = $2", [100, 1]);
await client.query("update accounts set balance = balance + $1 where id = $2", [100, 2]);
await client.query("commit");
} catch (error) {
await client.query("rollback");
throw error;
} finally {
client.release();
}
隔离级别
PostgreSQL 支持多个事务隔离级别。大多数业务使用默认的 read committed 即可。
begin isolation level repeatable read;
-- execute queries
commit;
在并发更新余额、库存、名额等数据时,需结合行锁或唯一约束处理竞争。
select id, stock
from public.products
where id = 1
for update;
常见问题
- 长事务会占用连接和锁,影响其他请求。
- 在事务中等待外部接口会放大锁持有时间,应尽量避免。
- 批量导入时单个事务过大可能导致回滚成本高,可以按批次提交。
- DDL 操作可能获取较强锁,生产环境应安排维护窗口。
与 HTTP API 的关系
单个 HTTP API 写请求通常由数据库在一次操作中完成。如果需要跨多条 SQL 保持事务一致性,建议封装为 RPC 函数,或在服务端使用 PostgreSQL 协议直连。
RPC 示例请参考 调用 RPC。