如何为 oracle 中的每个线程选择和阻止行?(PostgreSQL 有一个工作示例)
Posted
技术标签:
【中文标题】如何为 oracle 中的每个线程选择和阻止行?(PostgreSQL 有一个工作示例)【英文标题】:How can I select and block row for each thread in oracle?(There is a working example for PostgreSQL) 【发布时间】:2019-01-10 09:19:30 【问题描述】:我有桌子products
:
id name count description status expiration_date
1 Hz 1 Test1537208034036 NEW 2018-09-17
2 Pz 3 Test1537209516789 NEW 2018-10-17
3 Uz 7 Test1537210999618 NEW 2018-08-17
4 Mz 12 Test1537212483215 NEW 2018-11-17
我需要选择 count 且状态 = NEW 最大的行。为此,我可以在 postgreSQL 上写:
Select *
from products
where status = 'NEW'
order by count desc
LIMIT 1
但如果 4 个线程将开始执行此选择 - 每个线程将获得相等的行(计数 = 12)。我可以重写这个查询,它工作正常:
Select *
from products
where status = 'NEW'
order by count desc
LIMIT 1
for update of products skip locked
但我不能在 Oracle 中重复这一点。
SELECT p.*
from (
Select *
from products
where status = 'NEW'
order by count desc
) p
WHERE p.ROWNUM = 1
FOR UPDATE OF products SKIP LOCKED
Oracle 没有 LIMIT 1
和 rownum
的工作方式不同。我需要从 ordered 表中获取第一行(order by desc
)but this row not locked
。
我怎样才能像 PostgreSQL 一样重复逻辑。也许我的选择是错误的。
如果你看起来更轻松,这就是我想要的 - 我有桌子和很多线程。我需要每个线程从数据库中接收最旧的行(或最大的计数),并且只有它是一个。其他线程不应该收到它。下一个线程应该接收它之后最旧(或最大)的行。
【问题讨论】:
Oracle 有fetch first x rows only
而不是 limit
(实际上 Postgres 也支持这种语法)。但是您需要使用别名获取子选择的 rownum inside 并在 outside 上使用它。您当前的代码使用 outer 查询中的rownum
,而不是内部查询。
@a_horse_with_no_name Oracle 的一个例子并不准确。只是问题是 oracle 使用 2 选择而不是锁定行。如果更准确地说,那么 4 个线程阻塞同一行
如果我使用别名获取子选择内部的 rownum 并在外部使用它 - 所有线程都会立即执行第一个选择而不会阻塞。相反,如果我将按单行排序,因为内部选择会给我一行。我必须对所有行进行排序并选择一个然后阻止它
【参考方案1】:
我建议使用动态 sql。锁是在获取时获取的。您获取一条记录,它同时被锁定。请记住,一旦您完成事务 - 提交或回滚,记录就会解锁。
这是一个简短的函数示例,它为您的线程返回下一个products.id
。
create or replace function get_next_unlocked_id
return number
is
cRefCursor sys_refcursor;
rRecord products%rowtype;
begin
-- open cursor. No locks so far
open cRefCursor for
'select * from products '||
'where status = ''NEW'' '||
'order by count desc '||
'for update skip locked';
-- we fetch and lock at the same time one record
fetch cRefCursor into rRecord;
-- close cursor
close cRefCursor;
-- return ID or any other attribute(s)
return rRecord.id;
end;
【讨论】:
所有 4 个线程都生成open cursor. No locks so far
,当每个线程将 fetch cRefCursor into rRecord;
时,每个线程将锁定同一行,因为所有 4 个线程在第一次选择时得到相同的结果
例如我在表中有 5 个线程和 5 行。所有线程同时请求数据 - 每个线程进行选择并获取所有 5 行(有序),并且仅在第一行块之后。但是第二个线程尝试阻止来自相同数据的第一行...
他们得到的数据不同,他们得到的数据不同。这是“for update skip locked”的特性——测试一下。并且不要忘记将自动提交设置为 false,因为记录在提交时被解锁。
[72000][14551] ORA-14551: 无法在请求中执行 DML 操作。 ORA-06512:在“IP696.GET_NEXT_UNLOCKED_ID”,第 8 行
如果我删除这部分 for update skip locked
功能有效以上是关于如何为 oracle 中的每个线程选择和阻止行?(PostgreSQL 有一个工作示例)的主要内容,如果未能解决你的问题,请参考以下文章