【问题标题】:Oracle lock to calculate MAXoracle锁计算MAX
【发布时间】:2014-03-06 14:59:03
【问题描述】:


我需要找到一种安全且正确的方法来实现这一点:我在数据库中存储了一个操作日志,其中包含一个顺序的 EVENT_ID 数字列。我需要能够随时删除最后一行,并且 EVENT_ID 中的值不能跳过任何数字,因此我不能使用序列。
我写了一个返回 MAX(EVENT_ID)+1 的 PL/SQL 函数

CREATE OR REPLACE FUNCTION NEW_ID
(OP_TYPE       IN LOG_TABLE.OP_TYPE_ID%TYPE,
 APP_ID        IN LOG_TABLE.APPLICATION_ID%TYPE)
 RETURN LOG_TABLE.EVENT_ID%TYPE IS
  MYRES LOG_TABLE.EVENT_ID%TYPE;
BEGIN
  LOCK TABLE LOG_TABLE IN EXCLUSIVE MODE NOWAIT;
  SELECT MAX(LOG_TABLE.EVENT_ID) INTO MYRES
    FROM
      LOG_TABLE
    WHERE
      LOG_TABLE.OP_TYPE_ID = OP_TYPE
     AND
      LOG_TABLE.APPLICATION_ID = APP_ID;
  IF MYRES IS NULL THEN
    MYRES := 1;
  ELSE
    MYRES := MYRES + 1;
  END IF;
  RETURN MYRES;
END NEW_ID;
/

但是,有多个客户端在同一个数据库上运行和报告。这意味着如果两个或多个客户端在完全相同的时刻调用该函数,我最终可能会得到多个相等的 EVENT_ID 值。
AFAIK 锁定一个表并不会阻止它从其他会话中读取 - 有没有办法做到这一点?可以用光标吗?

【问题讨论】:

  • 这不是一件好事,因为这不会很好地扩展,但您可以使用 DBMS_LOCK 包来实现自己的锁定以防止多次访问。
  • 使用“序列表”,其中每个请求新数字的客户端锁定一行 (update seq_table set event_id = event_id + 1 returning event_id) 可能是更好的解决方案。如果将它与基表上的删除触发器结合使用,应该没问题。
  • 请记住。 Gapless => 不会扩展。从逻辑上讲,没有办法让“快速” -并行运行。
  • 但是我的函数不会真正解决问题本身吗?如果 LOCK 命令失败,那么整个函数应该。我说的对吗?
  • 当我删除最后一行并且有人使用 +1 id 添加时会发生什么?那么它将直接被破坏,因为删除的行与它的编号一起消失。我根本不相信这是可能的。

标签: oracle plsql


【解决方案1】:

很少有真正的业务需求需要无间隙序列,所以我认为您仍然应该考虑使用序列。如果您坚持在生成的 ID 中必须没有间隙,最好使用单独的 1 行表,您可以在不阻止读取 LOG_TABLE 的情况下独占锁定该表,您将在其中维护下一个可用的 EVENT_ID价值。

【讨论】:

  • 遗憾的是,海关法规要求此编号每行必须 100% 连续,否则可能会对运输代理处以罚款。
  • 好的,这是极少数情况之一。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多