【发布时间】: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 添加时会发生什么?那么它将直接被破坏,因为删除的行与它的编号一起消失。我根本不相信这是可能的。