我应该在交易中包含SELECT吗?

时间:2014-07-02 13:47:50

标签: database django postgresql transactions

使用数据库事务对多个更新进行分组时,我是否应该在事务中包含SELECT?例如,让我说:

  1. 获取记录
  2. 使用记录中的数据
  3. 检查该记录的编辑权限
  4. 更新一些记录
  5. 更新其他一些记录
  6. 我应该在"记录之前开始交易"阶段,或只是更新?

    我使用Postgres / Django transaction.atomic(),但我认为这不重要。

2 个答案:

答案 0 :(得分:4)

简短版本:"它取决于"。

长版:

如果您正在进行读取 - 修改 - 写入循环,那么它不仅必须在事务中,而且您必须SELECT ... FOR UPDATE您稍后要修改的任何记录。否则,您将面临丢失写入风险,在此情况下,您会覆盖其他人在您阅读记录和编写更新时所做的更新。

SERIALIZABLE事务隔离也可以帮助解决这个问题。

你真的需要了解并发性和隔离性。不幸的是,唯一的简单,容易"只做X"在不理解的情况下回答是通过锁定所涉及的所有表来开始每个事务。大多数人都不想这样做。

我建议the tx isolation docs阅读(或两个,三个或四个 - 它的硬材料)。尝试并发psql个会话(多个终端)来创建竞争条件和冲突。

答案 1 :(得分:1)

理想情况下(,如果可能),您将在 data-modifying CTE中执行所有四个步骤(这会自动发生在内部单笔交易)。

这仍然不排除竞争条件,只是使它们不太可能,因为SELECT .. FOR UPDATE和后UPDATE之间的时间范围被最小化。 (是的,你仍然应该使用FOR UPDATE (or another appropriate locking level)来对抗严重的并发访问下的竞争条件。)

这不是像Django这样的Web框架的典型(低效)方法。但这是优越的方法。它以多种方式优化性能:

  • 减少到数据库服务器的往返次数(可能是最重要的)
  • 最小化锁定时间
  • 允许Postgres优化查询

在数据修改CTE中使用SELECT .. FOR UPDATE时,请注意unreferenced CTEs are not executed at all,它也不会按预期锁定行。

数据修改CTE的代码示例:

There are many more on SO. Try a seach.