在PostgreSQL表上工作的并发进程

时间:2013-04-10 22:09:17

标签: postgresql concurrency postgresql-9.1

我有一个简单的过程,我需要处理一个表的记录,理想情况下运行该过程的多个实例而不处理相同的记录。我使用MySQL完成此操作的方式相当普遍(尽管我认为令牌字段更像是黑客):

在表格中添加几个字段:

CREATE TABLE records (
    id INTEGER PRIMARY KEY AUTO_INCREMENT,
    ...actual fields...

    processed_at DATETIME DEFAULT NULL,
    process_token TEXT DEFAULT NULL
);

然后是一个简单的处理脚本:

process_salt = md5(rand()) # or something like a process id

def get_record():
    token = md5(microtime + process_salt)
    db.exec("UPDATE records SET process_token = ?
             WHERE processed_at IS NULL LIMIT 1", token)
    return db.exec("SELECT * FROM records WHERE token = ?", token)

while (row = get_record()) is valid:
    # ...do processing on row...

    db.exec("UPDATE records SET processed_at = NOW(), token = NULL
             WHERE id = ?", row.id)

我正在使用PostgreSQL数据库的系统中实现这样的过程。由于MVCC,我知道Pg可以被认为比锁定更成熟 - 我可以在Pg而不是令牌字段中使用行锁定或其他功能吗?

2 个答案:

答案 0 :(得分:1)

这种方法适用于PostgreSQL,但是当你每次更新两行时,它往往效率很低 - 每次更新需要两个事务,两次提交。使用commit_delay并可能禁用synchronous_commit可以稍微降低成本,但除非您的存储子系统上有非易失性回写缓存,否则它仍然不会很快。 / p>

更重要的是,因为你提交了第一个更新,所以无法区分仍在工作的工人和已经崩溃的工人之间的区别。如果所有工作人员都在本地计算机上,然后偶尔扫描丢失的PID,那么您可以将令牌设置为工作人员的进程ID,但这很麻烦且容易出现竞争条件,更不用说重复使用pid的问题了。

我建议您采用旨在解决这些问题的真实排队解决方案,如ActiveMQ,RabbitMQ,ZeroMQ等。PGQ也可能具有重要意义。< / p>

在事务性关系数据库中进行队列处理应该很容易,但实际上很难做得好并且做得对。在详细检查时,大多数看起来合理的“解决方案”实际上都会将所有工作序列化(因此只有一个排队工作人员在任何给定时间做任何事情)。

答案 1 :(得分:0)

您可以使用SELECT ... FOR UPDATE NOWAIT获取行上的独占锁定,如果已锁定则报告错误。