【问题标题】:How to best avoid Multiple Inserts?如何最好地避免多次插入?
【发布时间】:2009-03-19 07:24:48
【问题描述】:

我有一个监视目录更改的服务应用程序。

服务应用程序将密切监视目录。它是这样做的:

  1. 睡眠 X 分钟
  2. 扫描目录,看看是否有新添加
  3. 每次添加启动一个线程
  4. 重复 1-3

它所做的一件事是将记录插入数据库。现在,由于可以同时运行多个线程,完全有可能两个线程同时插入记录。

我想避免这种同时插入;相反,我希望这些插入排队,这意味着只有 1 个线程可以在单个时间点访问数据库。当区域已经有1个线程访问数据库时,其他服务无法访问数据库。

我想要这个的原因是因为在电涌的情况下,我只丢失了一个交易而不是很多。

我正在考虑使用锁定数据库事务的 lock 语句。这是最好的方法吗?

P/S:我正在编写一个与 MySQL 数据库对话的 .Net 服务。

【问题讨论】:

  • 为什么在文件更改时启动服务?为什么不让服务使用 FileSystemWatcher 监视目录= 如果它真的是一个服务,它应该在后台执行并在发生某些事情时唤醒。然后你可以控制平行插入到你心中的愿望......
  • 我编辑问题以使其更清晰。

标签: database-design windows-services


【解决方案1】:

数据库意味着允许多个进程同时插入数据,所以我看不到你的问题。有错误吗?

[编辑] 你害怕电涌。在哪里?在服务器还是客户端?以下是您的选择:

  1. 服务器崩溃。只需让客户端检查“连接丢失”的错误代码并让他们重试插入,直到成功。要找出此错误代码,请在插入过程中关闭服务器或拔出客户端的网络电缆(或两者兼而有之;有时,由于意外的网络问题,您会收到不同的错误)。

  2. 客户端崩溃(更有可能,因为客户端通常是廉价 PC。如果用户可以访问它,它最终会感染病毒或文件系统损坏,它会耗尽 RAM,有人会安装一些“酷”的东西,它会破坏一个重要的 DLL,或者诸如此类的东西)。在这种情况下,您必须再次启动客户端,检查已插入的内容(使用应用程序密钥)并从那里继续。

应用程序键是与您的业务流程相关的行的 ID。例如,如果您销售卡片,则应用密钥可以是汽车名称和客户名称加上时间戳。

【讨论】:

  • 我不希望同时插入,因为如果有电涌,我不希望所有插入都付诸东流。
  • 按顺序执行时也会发生同样的情况...所以我真的不知道您的方法对您有什么好处...
  • 从字面上看,几十年 的工作已经投入到使数据库擅长处理这类事情(查找“ACID”)。数据库不会成为您的问题。
【解决方案2】:

我会先看看你为什么要这样做。通常应用程序是这样运行的,两个客户端同时进行插入没有问题(好吧,除了代码中的错误方法)。

此外,解决方案也会因场景而异。一种选择是拥有一个 microsoft 消息队列 (MSMQ),并将插入从这些服务中移出,因此插入的负载由从队列中读取的进程控制。

更新 1:我仍然不明白您为什么要避免插入并行运行(以及从我认为其他人所做的其他回复中读到的内容)。我将在此引用您的 2 个 cmets:

我想要这个的原因是因为在电涌的情况下,我只丢失了一笔交易,而不是很多。

蚂蚁:

这是因为在插入之前,还有其他需要并行的冗长作业。只有在插入期间,才需要顺序访问。

如果我单独阅读第一个,我实际上会认为这是希望它们并行的原因。你想尽快完成它们。顺序执行实际上会增加插入的时间,因此会有更多的时间发生电涌。

阅读第二个,它让我认为您可能更关心这些进程并行运行以及插入未进入数据库的影响。这再次意味着您希望尽快完成插入,因此没有理由按顺序进行。

对于内置支持,您可能需要包含文件系统的分布式事务(ms 正在查看文件系统的东西,但我不记得他们是否曾在较新的操作系统上做过某些事情) .但是,分布式事务的设置有点麻烦(msdtc 和对其使用的端口的访问)。

我遵循的一条好方法是向流程添加更多信息,以便能够判断失败的位置。您甚至可能还没有编写自动恢复过程的代码,但至少您知道您将获得肯定知道出现问题的信息。

最简单的方法是在进程开始时插入,并在完成时有一个标志来表示。如果这是一个长时间运行的过程,您可能希望拥有更像是您不断更新的状态的东西,以便能够判断它在哪个步骤中失败了。另一种方法是将状态写入文件系统。

无论如何,它只会告诉您最后一步成功完成,而不是当前步骤是否能够完成。这就是让重试逻辑更加复杂的原因,因为您不能只从停止的地方继续,您必须检查最后一步是否完成,这取决于每个步骤。

附言。如果是上述情况,很难从问题中分辨出来。您可能想针对长时间运行的进程和/或自动重试提出不同的问题。

【讨论】:

    【解决方案3】:

    如果您安装了多个 Windows 服务来监视同一个文件夹,或者如果您有一个多线程的 Windows 服务,那么您将获得多个同时插入。否则你不必担心。

    【讨论】:

    • 是的,情况就是这样:我有多个服务同时运行。
    【解决方案4】:

    为什么每次插入都创建一个新线程?为什么不简单地循环插入?只有在需要并行性时才需要线程;在你的情况下,这似乎与你想要的完全相反。

    [编辑]

    这是因为在插入之前,还有其他需要并行的冗长作业。只有在插入期间,才需要顺序访问。

    然后让这些线程向主线程报告,后者收集结果并将它们插入循环中。

    【讨论】:

    • 因为在插入之前,还有其他冗长的工作需要并行。只有在插入期间,才需要顺序访问。
    【解决方案5】:

    正如许多人之前所说:这是一件坏事。它会人为地造成瓶颈。

    但如果你真的想这样做,这里有你的选择:

    a) 在表上创建一个主键。无论如何,您都应该这样做以获得干净的数据库设计。为了检查 PK 的有效性,数据库必须使用锁定,这将强制您插入到序列中。

    b) 如果 a) 不足以满足您的目的,您应该使用单独的锁。根据您的环境,可能会有一个特殊的 api(例如 java 并发 api)。

    c) 如果它不可用或者你不喜欢它,你可以使用数据库的锁定机制:创建一个只有一行的表。在每个插入上执行以下操作: - 阅读特殊表格“更新”。这将(在大多数数据库设置中)阻止任何其他会话执行相同操作。 - 做插入。 - 犯罪。这将释放锁定并允许下一个会话继续。

    提醒一下:这与人们试图正常实现的目标完全相反:允许尽可能多的线程在不受干扰的情况下工作

    【讨论】:

      【解决方案6】:

      我认为这里最好的选择是遵循 Aaron 的建议,并在处理完每个文件后让线程报告;然后从单线程“管理器”类更新数据库。

      如果这不是一个选项,您可以在更新数据库的代码周围使用 lock/synclock。

      但是,您的一位 cmets 说有多个服务正在运行。真的吗?多个 Windows 服务监视同一个目录和多线程处理?为什么?我认为,您必须更好地捍卫该架构才能获得更好的响应。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-20
        • 1970-01-01
        • 2023-03-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多