多线程处理数据库记录的最佳实践

Posted

技术标签:

【中文标题】多线程处理数据库记录的最佳实践【英文标题】:Best practices for multithreaded processing of database records 【发布时间】:2010-10-08 10:43:06 【问题描述】:

我有一个进程查询表中PROCESS_IND = 'N' 的记录,进行一些处理,然后将PROCESS_IND 更新为'Y'。

我希望允许此进程的多个实例运行,但不知道避免并发问题的最佳做法是什么。

我应该从哪里开始?

【问题讨论】:

PROCESS_IND 可以是任何其他值,例如 'L' 吗? 当然,它只被这个进程使用,所以它可以是任何东西 发布页面大小、表结构、#rows 和索引会有所帮助。 【参考方案1】:

您应该在桌子上启用row level locking

CREATE TABLE mytable (...) LOCK DATAROWS

那么你:

开始交易 使用FOR UPDATE 选项选择您的行(这将锁定它) 随心所欲。

在事务结束之前,没有其他进程可以对此行执行任何操作。

P. S. 有人提到使用LOCK DATAROWS 可能导致的开销问题。

是的,有开销,但对于这样的表,我几乎不会称其为问题。

但是,如果您切换到DATAPAGES,那么您可能只锁定每个PAGE 的一行(默认为2k),并且行驻留在一页中的进程将无法同时运行。

如果我们谈论的是同时锁定了十几行的表,那么几乎不会有任何明显的性能下降。

进程并发对于这样的设计来说更为重要。

【讨论】:

我们不知道足够的信息来调用“这样的表”,我们不知道页面大小、每页有多少行、表或结构的大小或其他查询/进程在桌子上。我已经看到应用程序通过行级锁的实施来满足他们的需求 当然我们什么都不知道,但让我们做一个过早的优化,以防万一:) 我不确定你的意思?立即转向行级锁定并不是过早的优化。它只应非常谨慎地使用,通常仅在无法修改代码并且存在无法通过其他方式解决的死锁时使用。【参考方案2】:

将过程转换为单个 SQL 语句,并将多行作为单个批处理进行处理。这就是数据库的工作方式。

【讨论】:

虽然有很多进程无法在数据库中运行。【参考方案3】:

最明显的方式就是加锁,如果你的数据库没有锁,你可以自己加一个“Locked”字段来实现。

一些简化并发的方法是随机访问未处理的项目,所以他们不是在第一个项目上竞争,而是随机分配访问。

【讨论】:

【参考方案4】:

虽然我理解我的意图,但我不同意立即进行行级锁定。这将减少您的响应时间,实际上可能会使您的情况变得更糟。如果在测试后您发现 APL 的并发问题,您应该首先迭代移动到“数据页”锁定!

要真正正确地回答这个问题,需要更多关于表结构和所涉及的索引的信息,但需要进一步解释。

DOL,数据行锁定比所有页/页级锁定使用更多的锁。管理所有锁的开销以及由于在缓存中请求更多锁结构而导致的可用内存减少将降低性能并抵消您通过转向更多并发方法可能获得的任何收益。

测试您的方法,而不先在 APL 上移动(所有页面锁定“默认”),然后如果发现问题,则移动到 DOL(首先是数据页,然后是数据行)。请记住,当您将表切换到 DOL 时,该表上的所有响应都会稍微变差,该表使用更多空间并且该表变得更容易出现碎片,需要定期维护。

所以简而言之,不要直接转移到数据行,首先尝试你的并发方法,如果有问题,首先使用数据页锁定,然后是最后的数据行。

【讨论】:

【参考方案5】:

我使用的模式如下:

创建列“lockedby”和“locktime”,它们分别是线程/进程/机器 ID 和时间戳(在多台机器之间拆分处理时需要机器 ID)

每个任务都会执行如下查询:

UPDATE taskstable SET lockedby=(my id), locktime=now() WHERE lockedby IS NULL ORDER BY ID LIMIT 10

其中 10 是“批量大小”。

然后每个任务执行一个 SELECT 以找出它已“锁定”哪些行以进行处理,并处理这些行 每行完成后,将lockedby和locktime设置回NULL 所有这些都是在一个循环中完成的,尽可能多的批次。 cron 作业或计划任务会定期重置锁定时间过长的任何行的“lockedby”,因为它们可能是由已挂起或崩溃的任务完成的。然后会有其他人来接他们

LIMIT 10 是特定于 mysql 的,但其他数据库也有等效值。导入 ORDER BY 是为了避免查询不确定。

【讨论】:

+1 表示非常好的想法。我只是不知道为什么 ORDER BY 很重要,除非有一个必须处理行的顺序。如果是这样,那么多个进程是不可能的。 在 Sybase 中,您可以使用“set rowcount #rows | @variable”作为 LIMIT 的批处理大小。 ORDER BY 仅在您需要确定性 SQL 语句时才重要 - 在 MySQL 中,这对于在语句模式下重放二进制日志很重要,这对于复制和时间点至关重要恢复工作。在其他数据库中,它不太重要。 您应该阅读您的文档以了解如何获取进程/线程 ID。我敢肯定这很简单。 当两个任务同时发出并提交锁时会发生什么?如果此行为很关键,您可能需要考虑让单个调度程序任务完成所有锁定标记。到那时,工人任务只需要担心“我的是什么?”

以上是关于多线程处理数据库记录的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

多线程设计最佳实践

实践案例丨Netty案例集锦之多线程篇(续)

Java多线程并发最佳实践

在线程中处理在 catch 块中抛出的异常的最佳实践。 (。网)

[多线程]托管线程处理的最佳做法

处理 UIActivityIndi​​cator 和多线程的最佳方法是啥?