确保使用JDBC对数据库表进行原子读取的最佳实践是什么?

时间:2016-07-19 17:51:34

标签: database jdbc

我有一个java应用程序从数据库表读取要处理的作业,我可能有多个这个应用程序的实例在不同的服务器上运行,因为每个作业是独立的。一旦找到工作进行处理,其状态将更新为"运行"。我想确定从每个实例检索要处理的作业是原子的,我如何使用JDBC实现这一点?

6 个答案:

答案 0 :(得分:5)

一种完全通用的方法 * 虽然效率可能稍低,但可能会使用特定于服务器的标识符来声明&#​​34;声明"作业首先将其状态更新为该标识符,然后根据该值检索作业。例如,如果您在同一网络上使用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;

释放锁定。

此处an actual example

免责声明:SELECT FOR UPDATE是一种悲观锁定形式,其caveats as explained by Burleson。但是,如果客户端不是基于Web的并且非常并发,那么它可能是一个可行的解决方案。

答案 4 :(得分:1)

问题

准备好处理工作并以原子方式running进行处理。

解决方案

无需额外的锁。由于update操作在同一查询方面本身已经是原子操作(请参阅下面文档的摘录),更新jobs表,将状态running设置为准备好处理并获得此更新的结果 - 这将是您处理的工作。

示例:

的Postgres

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();
}

SQL Server

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