如何使用回滚事务轮询数据库队列表?

时间:2010-08-09 22:57:35

标签: java jdbc transactions queue atomic

我希望设置一个将用作消息队列的数据库表。该表将在其中插入带有唯一ID和时间戳以及状态为“PENDING”的消息。

假设正确处理了对此表的插入,我想知道使用简单的HSQLDB 2.0数据库从该表事务处理消息的最佳方法是什么(尽管此问题应该适用于所有支持事务的数据库)。 / p>

我希望读取状态为“pending”的下一条消息,并确保没有其他队列处理器也可以处理相同的记录,然后提交或回滚。

我提供了一些代码片段,说明我计划如何使用普通的旧JDBC实现此目的。

  • 这会有用吗?
  • 有更好的选择吗?

DDL:

create table message_queue (
    qidx integer,
    message varchar(120),
    status varchar(20),
    inserted_date timestamp,
    inserted_by varchar(20),
    processed_date timestamp,
    processed_by varchar(20),
)

insert into message_queue values (1,'Important message here','PENDING','2010-08-10 00:01:00', 'BOB', null,null)

这是我的队列读取SQL:

SET AUTOCOMMIT FALSE
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
START TRANSACTION
    DECLARE nextID INTEGER DEFAULT 0
    SET nextID = select max(qidx) from message_queue where status = 'PENDING' 
    update message_queue set status = 'CONSUMED' where QIDX = nextID
    select * from message_queue where QIDX = nextID
ROLLBACK

这是我的连接代码片段:

    conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
    try {
        String message = getNextMessage(conn); // uses sql in snippet
        processMessage(message);
        conn.commit(); // should commit
    } catch (Exception e) {
        conn.rollback(); // should rollback update
    }

2 个答案:

答案 0 :(得分:3)

在这些情况下使用的一般模式(跨数据库,没有锁,没有等待 - 准确的乐观锁):

  1. 多个工作人员中的一个从队列表中选择状态为“待定”。表包含version_number列(整数)。
  2. 工作人员通过其选择的项目列表,并尝试将每个项目更新为“正在进行中”状态(或其他)。 update语句的where子句包括“where version_number =”,显然是项目的主键。
  3. 如果更新语句在更新行数时返回1,则此工作程序成功保留该项目,并且可以继续工作。如果更新语句返回0行更新 - 这个项目是由其他一些工作者选择的,所以这个工作者应该默默地跳过它。

答案 1 :(得分:2)

当您必须考虑数据库的多用户特性时,排队绝对是一个非平凡的架构。一般来说,您不希望自己实现这些类型的东西。正如我相信您很快发现的那样,问题在于,当有多个读取器尝试从同一队列中读取时,您只需要一个进程来获取单个消息。也就是说,您必须确保仅处理一次消息。那么你就开始思考“好吧,好吧,我会更新那条记录,以便其他进程无法获得它”,但是你会意识到,在你提交之前,其他进程将无法看到你的变化。如果您锁定记录,那么在完成第一个处理之前,您不能从队列中读取另一个进程,基本上是序列化整个事务。

根据设计,队列事务必须与从队列中读取的进程的事务有些正交。不幸的是,这是每个数据库的实现将显着不同的一个领域。

也许更好的方法是使用另一个库(不一定是数据库)来进行消息传递。由于HSQLDB没有内置的排队支持,因此队列的不同(java)实现可能是这种情况的工具。