【问题标题】:Prevent other sessions from reading data until I'm finished在我完成之前阻止其他会话读取数据
【发布时间】:2014-07-09 16:04:26
【问题描述】:

在我完成之前阻止其他会话读取数据

我有一张桌子可以容纳来自不同公司的客户,例如:

CUSTOMER
    CUSTOMER_ID
    COMPANY_ID
    CUSTOMER_NAME
    FOO_CODE

当我插入或更新客户时,我需要根据现有客户(公司内部)计算 FOO_CODE。

如果我只是这样做:

SELECT MAX(FOO_CODE) AS GREATEST_CODE_SO_FAR
FROM CUSTOMER
WHERE COMPANY_ID=:company_id

...然后以客户端语言 (PHP) 生成代码,最后发出 INSERT/UPDATE 我知道如果其他程序实例获取相同的GREATEST_CODE_SO_FAR,我可能会面临竞争条件。

是否可以在表上发出行级锁定,以便尝试读取属于给定公司的任何客户的FOO_CODE 列的其他会话被延迟,直到我提交或回滚我的事务?


我的失败尝试:

  • 这个:

    SELECT MAX(FOO_CODE)
    FROM CUSTOMER 
    WHERE COMPANY_ID=:company_id
    FOR UPDATE
    

    ...触发器:

    ORA-01786: 不允许对此查询表达式进行 FOR UPDATE

  • 这个:

    SELECT FOO_CODE
    FROM CUSTOMER 
    WHERE COMPANY_ID=:company_id
    FOR UPDATE
    

    ...检索所有公司行,甚至不阻止其他会话读取数据。

  • LOCK TABLE...好吧,文档几乎没有任何示例,我无法弄清楚语法

附: 不是一个递增的数字吗,它是一个字母数字字符串。

【问题讨论】:

  • 你有 FOO_CODE 字段的唯一键吗?它可以防止在此列中具有相同的值。如果您有竞争条件,您将根据需要正确插入值多次重新启动程序。使用唯一密钥的解决方案也将扩大规模,因此您不会有不必要的锁和闩锁。
  • @zaratustra - 我没有,但无论我接下来做什么,我都需要修复一个明显的遗漏。
  • 另外,如果你不向用户显示这个字段,你可以使用函数SYS_GUID()生成FOO_CODE,它非常可靠,即使在具有RAC架构的系统中也可以使用它
  • 我正在阅读Generating A Gap-free Series Of Numbers — Not Always A Problem,看起来很有希望。

标签: sql oracle oracle10g


【解决方案1】:

据我所知,您不能阻止另一个会话读取数据。 Oracle 与其他一些数据库之间的区别之一是编写器不会阻止读取器。

我可能会对此略有不同。我假设您生成下一个foo_code 的方式是确定性的。如果您在 company_id, foo_code 上添加唯一索引,那么您可以让您的应用程序尝试循环插入:

  • 获取当前最大值
  • 计算您的新代码
  • 插入
  • 如果没有违反约束,请跳出循环
  • 否则继续循环的下一次迭代并重复该过程

如果两个会话同时尝试此操作,那么第二个会话将尝试插入相同的 foo_code 并获得唯一的约束违规。这被困住并得到很好的处理,它只是再试一次;可能多次,直到它得到一个干净的插入。

您可以有一个尝试在循环中插入的 DB 过程,但由于您想在 PHP 中生成新值,那么循环也在 PHP 中尝试简单插入是有意义的。

如果您有大量插入并且可能发生冲突,这不一定能很好地扩展。但是,如果您预计同一客户同时插入的情况很少见,并且只需要在发生这种情况时处理奇​​怪的情况,这不会增加太多开销。

【讨论】:

  • 感谢您的洞察力。无论如何,我都需要添加唯一索引(太糟糕了,我正在处理别人的代码,最简单的任务可能令人生畏)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-31
  • 1970-01-01
  • 2015-07-19
相关资源
最近更新 更多