代码之家  ›  专栏  ›  技术社区  ›  SqlRyan

跨多个线程处理数据库队列-设计建议

  •  3
  • SqlRyan  · 技术社区  · 15 年前

    我有一个SQL Server表,上面满是我的程序需要“跟进”的命令(调用一个WebService来查看是否对它们做了什么)。我的应用程序是多线程的,可以在多个服务器上运行实例。目前,每隔一段时间(在线程计时器上),进程随机选择100行( ORDER BY NEWID() )从“未确认”订单列表中进行检查,标记任何成功返回的订单。

    问题是线程之间和不同进程之间有很多重叠,它们不能保证很快就会检查新订单。另外,有些订单永远不会被“确认”,而且是死的,这意味着它们妨碍了需要确认的订单,如果我不断地选择它们,就会减慢进程。

    我更喜欢的是系统地检查所有未完成的订单。我可以想到两种简单的方法:

    1. 应用程序一次提取一个要检查的订单,将它检查的最后一个订单作为参数传递,SQL Server将返回未确认的下一个订单。更多的数据库调用,但这可以确保在合理的时间内检查每个订单。但是,不同的服务器可能会连续地重复检查相同的顺序,这是不必要的。
    2. SQL Server跟踪它请求进程检查的最后一个顺序,可能是在表中,并为每个请求提供唯一的顺序,增加其计数器。这涉及到将最后一个订单存储在SQL中的某个地方,这是我想要避免的,但它还确保线程不会不必要地同时检查相同的订单。

    我还有其他的想法吗?这有道理吗?如果我需要澄清,请告诉我。


    结果:

    我最后做的是在表中添加一个带有已完成订单的lastcheckedforconfirmation列,并添加一个存储过程来更新单个, 未经证实的 用getDate()行,并写出订单号,以便我的流程检查它。它将尽可能多地旋转这些线程(给定进程愿意运行的线程数),并使用存储过程为每个线程获取一个新的orderNumber。

    为了解决“不要尝试行太多或行太旧”的问题,我这样做了:如果“上次尝试以来的时间”>“创建到上次尝试之间的时间”,则SP将只返回一行,因此每次重试前它将花费两倍的时间-首先等待5秒,然后等待10秒,然后等待20、40、80、120,然后尝试15次(6小时我们的),它放弃了那个订单,SP将再也不会返回它。

    谢谢你们的帮助,各位-我知道我的方法不太理想,我很感谢你们的指点。

    3 回复  |  直到 15 年前
        1
  •  7
  •   Remus Rusanu    15 年前

    我建议阅读并内化 Using tables as Queues .

    如果将数据用作队列,则 必须 正确组织排队操作。我链接的文章详细介绍了如何做到这一点,您拥有的是挂起队列的变体。

    你必须彻底摆脱的一件事是随机性。如果有一件事很难在查询中重现,那就是随机性。 ORDER BY NEWID() 扫描每一行,生成一个guid,然后排序,然后返回前100个 . 在任何情况下,您都不能让每个工作线程每次扫描整个表,您将随着未处理的条目数的增加而杀死服务器。

    而是使用挂起处理日期。使用我在链接文章中所展示的技术,让队列按处理日期列(当项目需要重试时)进行组织(集群)和出列。如果要重试,出列应该 推迟 而不是删除它。 WITH (...) UPDATE SET due_date = dateadd(day, 1, getutcdate()) ...

        2
  •  2
  •   Andomar    15 年前

    最明显的方法是添加一列 LastCheckDt 服从命令。在每个线程中,检索最长时间的顺序,而不进行检查。在检索顺序的过程中,更新 LAST CHECKDT 字段。

    我不会一次检索100个订单,在线程到达数据库之前,数据库中的第50个订单可能会发生更改。得到一份订单,完成后,得到下一份。

    另外,我最初开发的过程没有多线程。检查未结订单的速度通常足以按顺序完成。

        3
  •  0
  •   csauve    15 年前

    您可能需要考虑的一个策略是这样的一个表;

    jobid bigint pk不为空,workerid int/nvarchar(max)为空

    其中worker是正在处理它的服务器的ID/名称,如果没有人接收到作业,则为空。当服务器接收到一个作业时,它将自己的ID/名称放入该列中,该列指示其他人不要接收该作业。

    一个问题是,工作任务的服务器可能崩溃,使任务永远无法完成。您可以添加一个表示超时的日期列,该列是在工作人员将作业提取到现在+您认为合适的时间跨度时设置的。

    编辑:忘记提了,您要么需要删除到作业完成时,或有一个状态字段指示完成。另外一个字段可以指示使作业表通用的作业参数:即,不要只是为订单制定解决方案,而是创建一个可以处理将来需要的任何内容的作业管理器。