2017-08-04 43 views
1

线程安全的顺序是老问题,但可以肯定的我又一次问。 其实我已经在Oracle中创建的序列,例如有一张桌子和希望与多个线程使用多个Java虚拟机的所有过程中会击中它平行。创建JVM水平和DB

以下是序列存储过程只是想问这是否可以与多个JVM一起使用,并且始终为所有jvm中的线程提供唯一编号或者是否有任何微小的机会返回相同的序列号两个以上的调用?

create table sequenceTable (id int) 
insert into sequenceTable values (0) 

create procedure mySequence 
AS 
BEGIN 
    declare @seqNum int 
    declare @rowCount int 

    select @rowCount = 0 
    while(@rowCount = 0) 
    begin 
     select @seqNum = id from sequenceTable 
     update sequenceTable set id = id + 1 where id = @seqNum 
     select @rowCount = @@rowcount 
     print 'numbers of rows update %1!', @rowCount 
    end 
    SELECT @seqNum 
END 
+0

我不认为在你的过程中有任何同步 - 这意味着你的第一个'select'和'update'语句有一个竞赛 - 可以从另一个进程的并发选择中间选择和更新另一个过程,从而给你两个相同的值。 (顺便提一下,你的问题与Java无关)。 –

+0

同意@ M.Prokhorov - 这不是严格的Java问题。即便如此,为什么你不会简单地使用一个实际的序列?多线程Java应用程序多年来一直在使用这些应用程序 –

+0

因为db不提供序列sybase,并且此存储过程将由多个JVM和线程调用。此外,只有当条件与我相同版本的条件满足时,update才会起作用,否则它会增加新的值,我已经用来自Java单元测试的500次调用对它进行了测试,并且每次给出的结果对我来说都不错。我要求确认可能是一些事件发生。 – user460293

回答

1

如果您选择维持目前的要生成一个新的序列号,每次更新sequenceTable.id柱的设计,你需要确保:

  • “当前”进程得到一个排它锁将含有所需序列号
  • 然后“当前”进程更新所需的行和检索新更新后的值
  • “当前”过程中的行释放独占锁

虽然上述可以通过begin tran + update + select + commit tran来实现,它实际上是更容易单update声明一点,如:

create procedure mySequence 
AS 
begin 
    declare @seqNum int 

    update sequenceTable 
    set @seqNum = id + 1, 
      id  = id + 1 

    select @seqNum 
end 

update声明是其自身的事务做的更新id柱和@seqNum = id + 1的分配下update的事务中的排他锁进行。


请记住,排它锁将阻止获得价值新ID的其他进程;最终结果是,新的ID值的产生将是单线程/顺序

虽然这是从确保所有过程获得独特的价值的角度来看“好”,它意味着这个特殊update声明将成为一个瓶颈,如果你有多个进程击中update兼任。

在这种情况下(大量并发的update s),通过调用存储过程的次数可以减少一些争用;这可通过具有所述主叫进程请求一系列新的ID值来实现(例如,通过@increment作为输入参数提供给PROC,然后代替id + 1使用id + @increment),与调用进程然后知道它可以使用序列号(@[email protected]+1 )到@seqNum


显然(?)任何使用存储过程生成'next id'值的进程仅在以下情况下起作用:* ALL *进程a)始终调用proc获取新的id值和b)* ALL *进程只使用返回的id值通过proc(例如,他们不会生成他们自己的id值)。

如果应用程序没有遵循此过程(调用proc获取新的id值)的可能性,则可能需要考虑将唯一id值的创建推送到插入这些id值的表中;换句话说,修改目标表的ID列以包含identity属性;这消除了应用程序调用存储过程(以生成新的ID)的需要,并且它(仍然)确保为每个插入生成唯一的ID。

+0

不错!谢谢... – user460293

1

您可以在ASE中模拟序列。使用reserve_identity函数来实现所需的活动类型:

create table sequenceTable (id bigint identity) 
go 

create procedure mySequence AS 
begin 
    select reserve_identity('sequenceTable', 1) 
end 
go 

该解决方案是无阻塞确实产生最小的事务日志活动。

+0

这样可以保证即使使用多个JVM它也不会提供重复的值吗? – user460293

+0

您正在使用数据库中的常规IDENTITY机制。数据库应该保证你。但请记住 - 每个身份都需要自己的桌子。如果你想使用第二个身份 - 你需要为它创建一个下一个表。 –

+0

@ user460293;请注意,Adam已将'sequenceTable.id'列从'int'修改为'bigint identity';而'int'和'bigint'的使用由您来决定,'identity'属性必须提供给'reserve_identity()'函数才能正常工作 – markp