相同数据时的Oracle行锁更新
Posted
技术标签:
【中文标题】相同数据时的Oracle行锁更新【英文标题】:Orace Row Lock For Update When Same Data 【发布时间】:2019-08-12 13:18:43 【问题描述】:我的表中有 5 个优惠券代码,这些优惠券代码是相同的。如果 10 位客户同时应用优惠券代码 [FIRST5],那么我需要分别为 5 位客户更新优惠券为“LOCKED”和 CUST_ID。对于这种情况,我尝试在 SQL 下锁定行并在客户申请优惠券时获取 P_KEY 以更新状态和客户 ID。但我无法为各个客户更新最新的 P_KEY。请指教正确的做法。
SELECT P_KEY FROM
(SELECT P_KEY FROM COUPON_DETAILS WHERE COUPON_CODE = 'FIRST5'
AND (STATUS = 'UNLOCK' OR STATUS IS NULL))
WHERE ROWNUM = 1 FOR UPDATE;
P_KEY COUPON_CODE STATUS CUST_ID
1 FIRST5 UNLOCK
2 FIRST5 UNLOCK
3 FIRST5 UNLOCK
4 FIRST5 UNLOCK
5 FIRST5 UNLOCK
【问题讨论】:
你所做的似乎是正确的,我建议你可以将“select...for update and update COUPON_DETAILS in a stored proc/package”包装起来,并让调用程序调用这个存储过程 【参考方案1】:如果有 10 位顾客同时申请优惠码 [FIRST5],则 我需要将优惠券分别更新为“LOCKED”和 CUST_ID,仅适用于 5 位客户。
我不知道有什么好的纯 SQL 方法可以做到这一点,因为FOR UPDATE
子句不会影响查询的结果集。它只影响获取行的方式。
所以,你可能想试试这个:
SELECT p_key
FROM coupon_details
WHERE coupon_code = 'FIRST5'
AND (status = 'UNLOCK' OR status IS NULL)
AND rownum = 1
FOR UPDATE SKIP LOCKED;
有理由认为这将导致 Oracle 读取所有匹配的 coupon_details
行,跳过任何被锁定的行,然后在第一行之后停止。但这只有在 for update
子句之后应用 rownum=1
条件时才有效。
不幸的是,它的工作方式是首先应用rownum=1
条件,因为FOR UPDATE
仅在获取期间发生。因此,最终发生的是每个会话只查看第一行。如果未锁定,则返回 p_key
。但如果第一行被锁定,它不会返回任何数据。 (或者,对于您发布的查询,其中不包括 SKIP LOCKED
,第一个会话之后的会话将等待。)
您真正需要做的是选择所有行,然后获取它们(跳过锁定的行),然后在第一行之后停止。
为此,您需要 PL/SQL。这是一个例子:
DECLARE
c SYS_REFCURSOR;
l_key coupon_details.p_key%TYPE;
BEGIN
-- Open a cursor on all the coupon details that are available to lock
OPEN c FOR
SELECT p_key
FROM coupon_details
WHERE coupon_code = 'FIRST5'
AND (status = 'UNLOCK' OR status IS NULL)
FOR UPDATE SKIP LOCKED;
-- Fetch the first one. The (FOR UPDATE SKIP LOCKED) will ensure that
-- the one we fetch is not locked by another user and, after fetching,
-- will be locked by the current session.
FETCH c INTO l_key;
-- Do what you need with the locked row. In this example, we'll
-- just print some debug messages.
IF l_key IS NULL THEN
DBMS_OUTPUT.PUT_LINE('No free locks!');
ELSE
DBMS_OUTPUT.PUT_LINE('Locked key ' || l_key);
END IF;
-- Close the cursor
CLOSE c;
END;
...请务必在提交之前UPDATE coupon_details SET status = 'LOCKED' WHERE p_key = l_key
。
【讨论】:
以上是关于相同数据时的Oracle行锁更新的主要内容,如果未能解决你的问题,请参考以下文章