多线程安全的JDBC保存或更新

时间:2009-10-15 23:18:45

标签: java multithreading jdbc transactions

我们有一个JMS队列的作业状态,两个相同的进程从队列中拉出来通过JDBC保持状态。从队列中提取作业状态时,将检查数据库以查看作业是否已有行。如果是,则使用新状态更新现有行。如果没有,则为此初始状态创建一行。

我们看到的是,一小部分新作业正在两次添加到数据库中。我们非常肯定这是因为作业的初始状态很快就会出现状态更新 - 一个进程获得一个进程,另一个进程进入另一个进程。两个进程都会检查作业是否是新作业,并且由于尚未记录,因此都会为其创建记录。

所以,我的问题是,您将如何以与供应商无关的方式防止这种情况发生?可以在不锁定整个桌子的情况下完成吗?

编辑:对于那些说“架构”不健全的人 - 我同意,但不能自由改变它。

2 个答案:

答案 0 :(得分:4)

在JOB_ID上创建一个唯一约束,并在发生约束违例异常时重试保持状态。

话虽这么说,我认为你的架构是不合理的:如果两个进程从队列中提取消息,则无法保证它们会按队列顺序将它们写入数据库:一个消费者可能会慢一点,一个数据包可能会被丢弃,...,导致其他消费者首先保留后面的消息,导致他们被更早的状态覆盖。

防止这种情况的一种方法是在消息中包含序列号,仅在序列号符合预期时更新行,否则延迟更新(这很容易丢失消息,但是......)。 / p>

当然,最简单的方法就是只有一个消费者......

答案 1 :(得分:0)

JDBC连接不是线程安全的,所以没有什么可以做的。

“......从队列中拉出两个相同的进程以通过JDBC保持状态...”

我根本不明白这一点。为什么两个相同的过程拥有一个消息队列侦听器池会不会更好,每个侦听器都会处理登陆队列的消息?每个监听器都有自己的线程;每一个都是自己的交易。 Java EE应用程序服务器允许您配置消息侦听器池的大小以匹配负载。

我认为复制这样一个过程的设计是在寻找麻烦。

您还可以更改JDBC连接上的隔离级别。如果你使它成为SERIALIZABLE,你将以较慢的性能价格确保ACID。

由于它是一个异步过程,如果您发现侦听器无法跟上登陆队列的消息,性能将只是一个问题。如果是这种情况,您可以尝试增加侦听器池的大小,直到您有足够的容量来处理传入的消息。