【问题标题】:Multithreaded application with database read - each thread unique records具有数据库读取的多线程应用程序 - 每个线程唯一的记录
【发布时间】:2019-05-19 16:42:54
【问题描述】:

我有一个 .net 应用程序,它基本上每次(每 5 分钟)从数据库表中读取大约一百万条记录,进行一些处理并更新表,将记录标记为已处理。

目前,应用程序在单线程中运行,从 DB 表中获取前 4K 条记录,对其进行处理,更新记录,然后获取下一条。

我正在使用带有存储过程的 dapper。我正在使用 4K 记录进行检索以避免数据库表锁定。

在多个线程中检索记录并同时确保每个线程获得新的 4K 记录的最佳方法是什么?

我目前的想法是我首先只检索 1M 记录的 ID。按升序对 id 进行排序,并将它们分成 4K 批次,记住批次中的最低和最高 id。 然后在每个线程中,我将调用另一个存储过程,该过程将通过指定检索到的记录的最低和最高 id 来检索完整记录,然后进行处理等等。

有没有更好的模式我不知道?

【问题讨论】:

  • 如果数据库访问本身是“基本的” - 即。是 I/O 绑定的,不能通过并发访问进行优化 - 从/到单个 DB 访问流可能“更好”。这可以允许,例如,单个排序/步行者。
  • 此声明:“我正在使用 4K 记录进行检索以避免数据库表锁定。”这不是真的,这不是 DB Lock(假设 MS SQL Server)的工作方式,在您的实例中配置了您的 ISOLATION LEVEL 元素,您以哪种方式从该表中获取行等。有很多网络上有关它的信息,我的推荐:Kalen Delaney 博客和书籍以及 Paul Randall,两者都可以为您带来清晰的画面。
  • @GeovannyHernandez 本身不是为了阅读,但我跳过了有关更新记录部分的详细信息(这就是 4K 记录的原因)。使用包含需要标记为已处理的记录的所有 id 的 XML 参数调用存储过程。我记得读过 5K 是进行批量更新时表锁的限制。
  • @AlexDee,我知道这本身不是为了阅读,但我的意思是 MS SQL Server 已经准备好管理并发连接和 DML 操作,5K 的限制是第一次我听说,你不会把这个 5K 和 Lock scalation 混为一谈吗?
  • 更好的模式是使用单线程读取数据库。

标签: c# .net sql-server multithreading design-patterns


【解决方案1】:

我觉得这个问题很有趣,部分原因是我试图做一些原则上类似的事情,但也因为我还没有看到一个超级直观的行业标准解决方案。然而。

如果您正确编写 SQL 查询,您打算执行的操作将会奏效。 使用ROW_NUMBER / BETWEEN 应该是可以实现的。 我将在这里编写和记录其他一些替代方案以及好处/注意事项。

并行处理

我知道您想在 SQL Server 中执行此操作,但作为参考,Oracle 将其作为关键字实现,您可以并行查询内容。

文档:https://docs.oracle.com/cd/E11882_01/server.112/e25523/parallel002.htm

SQL 以不同的方式实现这一点,您必须通过更复杂的关键字显式打开它,并且您必须使用某个版本:

这里有一篇很好的文章:https://www.mssqltips.com/sqlservertip/4939/how-to-force-a-parallel-execution-plan-in-sql-server-2016/

您可以将并行处理与 SQL CLR 集成相结合,这将有效地完成您在 SQL 中尝试执行的操作,而 SQL 管理数据块,而不是您在线程中。

SQL CLR 集成

您可能会研究的一个不错的功能是在 SQL 服务器中执行 .net 代码。此处的文档:https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/introduction-to-sql-server-clr-integration

这基本上允许您在 SQL 服务器中运行 C# 代码 - 为您节省读取/处理/写入往返。他们也改进了与此相关的持续集成 - 此处的文档:https://docs.microsoft.com/en-us/sql/integration-services/sql-server-integration-services?view=sql-server-2017

不幸的是,查看 QoS/获取日志以防万一出现问题并不像在工作人员作业中处理这件事那么容易。

使用单线程(如果您从外部源读取)

只有满足某些条件时,并行性才对您有益。以下来自 Oracle 的文档,但它也适用于 MSSQL:https://docs.oracle.com/cd/B19306_01/server.102/b14223/usingpe.htm#DWHSG024

并行执行改进了以下方面的处理:

  • 需要大表扫描、连接或分区索引扫描的查询
  • 创建大型索引
  • 创建大表(包括实体化视图)
  • 批量插入、更新、合并和删除

还有设置/环境要求

并行执行使具有以下所有方面的系统受益 特点:

  • 对称多处理器 (SMP)、集群或大规模并行 系统
  • 足够的 I/O 带宽
  • 未充分利用或间歇性使用的 CPU(例如,系统 CPU 使用率通常低于 30%)
  • 足够的内存来支持额外的内存密集型进程, 例如排序、散列和 I/O 缓冲区

还有其他限制。当您使用多个线程来执行您建议的操作时,如果其中一个线程被杀死/未能做某事/引发异常等......您绝对需要处理它 - 以一种您一直保持到什么是您处理的最后一个索引 - 因此您可以重试其余记录。 使用单个线程变得更简单。

结论

假设数据库建模正确并且无法进一步优化我会说最简单的解决方案,单线程是最好的解决方案。更容易记录和跟踪错误,更容易实现重试逻辑,我会说这些远远超过了您从并行处理中看到的好处。您可能会查看并行处理位以进行批量更新,您将对数据库执行此操作,但除非您将在 SQL 中有一个 CLR DLL - 您将以并行方式调用它的方法,否则我看不到克服的好处。在您运行并行查询时,您的系统也必须以某种方式运行,以提高效率。

您当然可以将您的工作角色设计为异步的,并且不会阻止每个记录处理。所以你仍然是多线程的,但你的查询会在一个线程中发生。

编辑至结论

在今天与我的同事讨论后,值得补充的是,即使使用单线程方法,您也必须能够从失败中恢复,因此原则上具有多线程与单线程相比 恢复/正常失败的要求并记住您处理的内容不会改变。不过,您将如何恢复,因为您必须编写更复杂的代码来跟踪您的多个线程及其状态。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-24
    • 2010-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-22
    • 2012-12-14
    相关资源
    最近更新 更多