【问题标题】:How to block INSERT under READ_COMMITTED isolation?如何在 READ_COMMITTED 隔离下阻止 INSERT?
【发布时间】:2012-12-16 08:00:08
【问题描述】:

给定两个表:

  • Company [id, name, balance]
  • Employee [id, name, company_id]

我想删除一家公司及其依赖项,但我不能自动删除,因为在我的实际项目中,一家公司有多个级别的依赖项,这些依赖项过于复杂,无法压缩到单个 SQL 语句中。此外,看看https://stackoverflow.com/a/5598275/14731,我不确定单个 SQL 语句是否是原子的。

相反,我这样做:

  1. SELECT ... FOR UPDATE与公司相关的所有员工(锁定他们以防止在交易中修改)
  2. 逐个删除每个员工
  3. 删除母公司。

有一个问题:如何防止在第 1 步和第 2 步之间插入引用公司的新记录? 根据https://stackoverflow.com/a/3602125/14731 SELECT ... FOR UPDATE 不会阻止插入。理想情况下,我想在 Company 上加一把锁,而不是每一种可能指向它的依赖项都加一把锁。

更新: 我有一个想法,但我不确定它是否有意义。

如果有人想要插入指向公司的新记录,他们可能需要查找它以确保它存在。如果我在第 1 步之前在公司上SELECT ... FOR UPDATE,它应该阻止他们的读取,从而延迟随后的插入。我有两个问题:

  1. SELECT ... FOR UPDATE 会阻止正常的SELECT 查询吗?如果不是,我必须确保在查找公司时插入线程始终使用SELECT ... FOR UPDATE
  2. 这种方法行得通吗?

【问题讨论】:

  • 你不能把你的删除操作包装在一个事务中:开始事务,用一个语句删除所有员工,删除母公司,错误回滚,否则提交,结束事务?
  • 您使用的是哪个 DBMS?甲骨文? PostgreSQL? DB2?
  • @LordPeter,在您删除所有员工和删除公司之间,没有什么能阻止另一个线程插入新员工。
  • @a_horse_with_no_name,我正在寻找与数据库无关的解决方案。如果这不可行,请随时提供特定于数据库的解决方案。
  • 如果您只运行两个单一查询,那么插入新员工的机会将会降低。如果发生这种情况,那么您可能有 FK 约束,这将导致公司删除失败,因此您将回滚并重试。或者,只是级联公司删除以取出“孤儿”员工?程序方法很少是正确的方法。

标签: sql transactions sql-insert


【解决方案1】:

这是一种可能的解决方案:

  1. 插入新关联的线程应在 Company 上建立读锁(使用 REPEATABLE_READisolation 或 SELECT ... FOR SHARE
  2. 删除公司的线程应该使用SELECT ... FOR UPDATE写锁行。
  3. 写锁将等待所有未完成的读锁被释放,并在DELETE操作完成之前阻止建立新的读锁。

【讨论】:

    猜你喜欢
    • 2021-08-09
    • 2012-03-21
    • 1970-01-01
    • 2015-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多