【问题标题】:Concurrent processes working on a PostgreSQL table处理 PostgreSQL 表的并发进程
【发布时间】:2013-04-02 23:23:51
【问题描述】:

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

向表中添加几个字段:

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 在锁定方面可能被认为比 MySQL 更成熟 - 我可以在 Pg 中使用行锁定或其他一些功能来代替令牌字段吗?

【问题讨论】:

    标签: postgresql concurrency postgresql-9.1


    【解决方案1】:

    这种方法适用于 PostgreSQL,但由于您将每行更新两次,它往往效率很低 - 每次更新需要 两个 事务,两次提交。使用commit_delay 并可能禁用synchronous_commit 可以在一定程度上降低此成本,但除非您的存储子系统上有非易失性回写缓存,否则它仍然不会很快。

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

    我建议您采用真正的队列解决方案,旨在解决这些问题,如 ActiveMQ、RabbitMQ、ZeroMQ 等。PGQ 也可能会引起很大的兴趣。

    在事务性关系数据库中进行队列处理应该很容易,但在实践中要做好并正确处理是非常困难的。大多数一目了然的“解决方案”实际上是对所有工作进行了序列化(因此在任何给定时间,只有许多队列工作人员中的一个在做任何事情)在详细检查时。

    【讨论】:

    • 是的,这种方法总感觉就像用扳手敲钉子一样——我一直想研究 ZeroMQ 很长时间,所以我会采取这条路线。感谢您的洞察力!
    【解决方案2】:

    你可以使用SELECT ... FOR UPDATE NOWAIT获取该行的排他锁,如果已经被锁,则报错。

    【讨论】:

      猜你喜欢
      • 2021-09-15
      • 1970-01-01
      • 1970-01-01
      • 2012-10-27
      • 1970-01-01
      • 1970-01-01
      • 2017-01-05
      • 1970-01-01
      • 2016-05-07
      相关资源
      最近更新 更多