我有一个java应用程序从数据库表读取要处理的作业,我可能有多个这个应用程序的实例在不同的服务器上运行,因为每个作业是独立的。一旦找到工作进行处理,其状态将更新为"运行"。我想确定从每个实例检索要处理的作业是原子的,我如何使用JDBC实现这一点?
答案 0 :(得分:5)
一种完全通用的方法 * 虽然效率可能稍低,但可能会使用特定于服务器的标识符来声明"声明"作业首先将其状态更新为该标识符,然后根据该值检索作业。例如,如果您在同一网络上使用Windows服务器,那么它们的服务器名称将唯一标识它们。如果你的表看起来像
JobID JobName Status
----- ------- ---------
1 Job_A Completed
2 Job_B
3 Job_C
如果无人认领的作业的状态为NULL
,那么在SERVER1上运行的应用程序可以通过setAutoCommit(true)
后跟
UPDATE Jobs SET Status='SERVER1'
WHERE JobID IN (
SELECT TOP 1 JobID FROM Jobs
WHERE Status IS NULL
ORDER BY JobID)
如果ExecuteUpdate
返回0,则表示没有待处理的作业。如果它返回1,那么你可以使用
SELECT JobID, ... FROM Jobs WHERE Status='SERVER1'
然后将其状态更新为'运行'使用参数化查询,如
UPDATE Jobs SET Status='Running' WHERE JobID=?
您提供从上一个SELECT中检索到的JobID。
* (即,不依赖于任何特定的SQL扩展,显式锁定或事务处理)
答案 1 :(得分:2)
使用数据库服务器支持的任何机制锁定表。
例如,在Postgres中它将是:
LOCK yourtable;
在交易期间,它就是你的桌子。
其他数据库会有类似的东西。
答案 2 :(得分:1)
使用具有CONCUR_READ_ONLY和TYPE_FORWARD_ONLY的ResultSet。如果您的数据库jdbc驱动程序支持它,它将只返回您的选择时间的原子读取。
根据此documentation,(内部和外部变化可见性表格摘要)
仅向前游标仅显示您的阅读时间结果。 CONCUR_READ_ONLY将阻止您的内部更新。
答案 3 :(得分:1)
使用交易性质的数据库时,一种流行的做法是执行ROW-LEVEL LOCKING。行级锁可防止多个事务修改同一行。 SELECT for UPDATE是实现此效果的简单方法。假设你有一个进程表:
SELECT process_id, status
from processes
for UPDATE of status SKIP LOCKED;
完成处理后,发出
update processes set status = 'updated'
where process_id = :process_id; --from before
问题
commit;
释放锁定。
免责声明:SELECT FOR UPDATE是一种悲观锁定形式,其caveats as explained by Burleson。但是,如果客户端不是基于Web的并且非常并发,那么它可能是一个可行的解决方案。
答案 4 :(得分:1)
准备好处理工作并以原子方式running
进行处理。
无需额外的锁。由于update
操作在同一查询方面本身已经是原子操作(请参阅下面文档的摘录),更新jobs
表,将状态running
设置为准备好处理并获得此更新的结果 - 这将是您处理的工作。
示例:
UPDATE jobs SET status = 'running'
WHERE status is NULL
RETURNING id;
就 JDBC 而言,您可以这样做:
String sql = "update ... returning ...";
boolean hasResult = statement.execute(sql);
if (hasResult) {
ResultSet rs = statement.getResult();
}
UPDATE jobs SET status = 'running'
WHERE status is NULL
OUTPUT UPDATED.id;
摘自Postgres documentation,其中显示了在使用相同查询的同一个表上执行UPDATE时2个事务的行为:
UPDATE只会查找从命令start开始提交的目标行 时间。但是,这样的目标行可能已经更新(或 当它被另一个并发事务删除或锁定时 找到。在这种情况下,可能的更新程序将等待第一个 更新事务以提交或回滚(如果它仍在 进度)。
答案 5 :(得分:0)
如果您想在特定示例中确保在并发环境中正常工作,可以使用服务器名称。
该表格如下:
JobID JobName Server Status
----- ------- ------- ---------
1 Job_A host-1 Completed
2 Job_A host-2 Working
3 Job_B host-3 Working
如果同一主机上有多个实例,也要添加进程ID:
JobID JobName Server ProcessID Status
----- ------- ------- ---------- ---------
1 Job_A host-1 1000 Completed
2 Job_A host-2 1000 Working
3 Job_A host-2 1001 Working
5 Job_B host-3 1000 Working