我有两个表,foo
和bar
:
foo
,----------------.
|id | name | ... |
|---+------+-----|
| 1 | abc | ... |
| 2 | xyz | ... |
| ... |
`----------------'
bar
,-------------------------.
| id | foo_id | num | ... |
|----+--------+-----+-----|
| 1 | 1 | 1 | ... |
| 2 | 1 | 2 | ... |
| 3 | 1 | 3 | ... |
| 4 | 2 | 1 | ... |
| 5 | 2 | 2 | ... |
| ... |
`-------------------------'
对于每个foo
记录,有许多bar
条记录,每条记录都有一个插槽号(num
),每个foo_id
的值应为1 - N }。因此,(foo_id
,num
)上有一个唯一键。这些插槽号是有意义的,必须是顺序的。
由于最后一个约束条件,我不能使用Oracle的原生序列(因为它们不能保证是连续的,并且似乎不是用于值的有意义的)。 foo
记录的数量也 extermely 大,并且每个foo_id
都有一个序列似乎非常不切实际。
所以,我的问题是 - 在插入时创建这些数字有哪些方法可以在高并发性下保留?我唯一的选择是锁定并选择MAX(num) + 1
,并在提交时释放锁定吗?
答案 0 :(得分:2)
你可以使用两步法:
运行一个脚本,该序列将填充序列号中的num:
--q1
insert into bar (id, foo_id, seq_num) values (:id, :foo, someseq.next_val)
--q2
update bar b1 set
num = (select 1+count(*) from bar b2
where b2.foo_id = b1._foo_id
and b2.seq_num < b1.seq_num)
where num is null
或者你可以让条形有间隙,但是有一个视图bar_vw可以在飞行中纠正它:
create view bar_vw as
select id, foo_id,
row_number() over (partition by foo_id, order by seq_num) as num
from bar
答案 1 :(得分:0)
如果有一种方法来构建系统,使得一次只有一个会话为任何特定的bar
行插入foo
行,那将是理想的,因为这将消除实际争用(I假设您仍然希望代码处理它以防万一)。
要管理并发性,您几乎肯定需要锁定父foo
行(select for update
),然后使用max(num) + 1
行执行bar
{ {1}}。我想你可能会省略锁定foo_id
记录的步骤,并创建一个物化视图,在提交时快速刷新以验证foo
值是顺序的,但这似乎不大可能是更快,似乎很可能它会使代码更复杂,因为你现在正在提交时捕获异常,而不是简单地阻塞,直到你可以继续前进。