跳到主要内容

事务

事务用于保证多条 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