编程后端开发者

详细介绍一下SQL中事务的工作特点。如何在不同会话同时访问同一表时控制数据的完整性?

用 Hintsage AI 助手通过面试

答案

在SQL中,事务允许将多个操作(插入/更新/删除)组合成一个原子工作单位,可以完全执行或完全撤销。事务的生命周期基于以下命令:

  • BEGINSTART TRANSACTION — 开始事务;
  • COMMIT — 确认更改;
  • ROLLBACK — 撤销事务中的所有更改。

SQL支持事务的隔离级别(未提交读、已提交读、可重复读、可串行化),这些级别决定了并行事务之间的数据可见性,并保护免受诸如“脏读”或“幻行”等问题影响。

为了控制数据的完整性,需要合理地:

  • 选择隔离级别(例如,对于银行应用程序——可能是可串行化的)。
  • 明确管理事务,特别是在同时编辑一个实体的地方(例如,SELECT ... FOR UPDATE)。

PostgreSQL示例:

BEGIN; -- 获取并锁定产品行 SELECT * FROM inventory WHERE id = 1 FOR UPDATE; UPDATE inventory SET quantity = quantity - 1 WHERE id = 1; COMMIT;

陷阱问题

流行数据库管理系统(PostgreSQL, MySQL)默认的隔离级别是什么?它与可串行化的有什么不同?

答案:
在PostgreSQL中,默认使用Read Committed隔离级别——在此级别下,事务只能看到请求时已提交的数据,但可能会出现“不可重复读”(non-repeatable reads)。
在MySQL(InnoDB)中,是Repeatable Read。与Serializable的区别在于,只有后者完全防止任何幻行或并行更改,但由于全局锁的原因,其性能明显较慢。

示例:

-- 在可重复读中,SELECT可以返回相同的行,而在已提交读中,在两个SELECT之间可能会出现新行。

历史

在大型金融系统中,在低隔离级别(已提交读)下进行大型账户转账时,偶尔出现同一余额被多个事务同时使用的情况。这导致了双重支出(竞争条件)。在切换到Serializable并合理管理锁后,问题消失了。


历史

在电子商务中,未封装在事务中的UPDATE product SET stock = stock - 1导致销售超过库存的商品。问题仅在大量竞争订单出现时显现。解决方案是使用事务和通过SELECT ... FOR UPDATE来锁定行。


历史

在物流系统中,某个表在频繁更新时忘记了显式提交。在故障的情况下,部分数据因自动提交或不正确的回滚而丢失。结果是记录丢失和代价高昂的审计。