【问题标题】:Executing interdependent Database ops on Multiple Threads在多个线程上执行相互依赖的数据库操作
【发布时间】:2013-03-13 21:35:42
【问题描述】:

我用 C# 编写的服务器应用程序每次需要从数据库中插入或删除数据时都会启动一个新线程。问题在于,由于线程的执行是任意的,因此如果这些事件几乎同时发生,则无法确保在插入同一对象后执行删除命令。

例如:服务器收到插入多个对象的命令。插入所有对象大约需要 5 秒钟。执行 1 秒后,服务器收到从数据库中再次删除所有这些对象的命令。由于删除可能在所有对象完全存储之前发生,因此结果未知。

如何管理某个线程的执行顺序?

【问题讨论】:

  • 寻找System.Threading.MutexSystem.Threading.ManualResetEvent
  • Threading.Mutex 是在特定调用中序列化线程的好主意。您还可以查看消息队列。
  • 我不认为使用互斥锁会起作用,因为可能存在相同数据的插入、删除和另一个插入操作。在第一次插入完成后,互斥锁不会确保在下一次插入之前执行删除命令,还是我错了?消息队列是一种选择,但我希望有更好的解决方案来避免性能下降。

标签: c# sql multithreading parallel-processing


【解决方案1】:

您可以为此使用事务并为不同的操作指定不同的级别。

例如,您可以将最高级别的事务用于写入/更新/删除,而将低级别的事务用于读取。与表相比,您还可以对其进行微调以允许仅阻止特定行。 Specific terminology 取决于您使用的数据库和数据访问库。

我建议不要使用任何排序。并行和有序只是不能很好地结合在一起。例如:

  • 您需要水平扩展服务器,一旦添加第二台服务器和负载平衡器,互斥锁解决方案将无法正常工作

  • 在大型分布式系统中,消息队列也无法正常工作,因为当一个线程完成扫描并决定我们可以开始时,另一个线程可以写入一条本应阻止操作执行的消息。此外,鉴于您的负载很高,多次扫描同一个队列效率低下。

【讨论】:

  • 应该阻止操作执行的操作是什么意思?插入后删除?在事务中,如果它们不在同一个事务中,也可能是这种情况。如果队列在清空之前不被扫描,我也不认为这里有任何低效率。假设它一开始有 500 个项目,只有 500 个项目出队,即使同时有更多项目到达,它们也必须等待下一次迭代。
  • 在事务中更新的行将被锁定在数据库端。情况1:插入首先带有预定义的ids = {1,2,3},而在执行时,ids = {4,5,6} 出现了删除。在这种情况下,两个事务可以在没有锁定的情况下同时运行并且都成功。情况 2:insert 首先带有预定义的ids = {1,2,3},而在执行时,ids = {2,3} 会出现 delete。在这种情况下,两个事务将不会同时运行。第一个插入将锁定三行,第二个事务将等待插入完成或失败并出现错误
【解决方案2】:

如果您知道您在删除之前收到了插入,而问题只是您不想中断插入,那么您可以在插入代码上使用锁定。

static object m_Lock = new object();

public void Insert()    
{
   lock (m_Lock)
   {
      InsertRecords();
   }
}

public void Remove()    
{
   lock (m_Lock)
   {
      RemoveRecords();
   }
}

这样您就可以确保在插入期间不会发生删除。

附:尽管您需要立即插入然后删除,但这似乎很奇怪。

【讨论】:

  • 我认为对于大量请求来说,锁定每个插入可能会导致性能下降。
  • @omerschleifer:这实际上取决于负载。如果服务器每秒收到很多请求,这当然会成为一个问题,因为所有请求都会一个一个地执行。
  • 这需要为每个可以插入/删除的对象使用唯一的方法。否则,任何对象的锁定插入/删除都会降低性能,因为不同对象可能有多个插入/删除。由于这是一个分布式应用程序,其中多个客户端可以处理相同的数据,所以图中的场景可以随时发生
  • 永远不要锁定 I/O(包括 DB 操作),因为您无法控制它的运行时间
【解决方案3】:

我认为最简单的方法是将所有传入请求排队以在一个集合中插入对象,并将所有传入请求排队以删除第二个集合中的对象。

服务器应该有一个基本循环:

一个。检查是否有传入的插入,如果有 -> 执行所有插入。

b.检查是否有传入的删除请求,如果有 -> 执行所有删除请求。

c.睡眠 X 毫秒。

现在,如果您对不存在的对象有删除请求。 你有两个选择:

一个。忽略此请求并将其丢弃。

b.忽略这一轮的这个请求,并在接下来的 N 轮中将其保留在集合中, 在删除之前(最后删除 - 假设这只是一个错误的请求,而不是竞争条件的问题。)

【讨论】:

  • 这不起作用,除非对传入的操作施加严格的限制。没有什么可以阻止在插入集中两次插入语义上相同的记录,并且该记录的中间删除被延迟。
  • 据我了解,问题是关于同步可能来自 odrer 的请求,并且由于它们可能来自不同的客户端,因此我提供了一个可扩展到客户端的解决方案。我没有提到数据完整性,因为你是对的。我不理解重复插入是这里的问题
  • 我同意所提供的问题不允许提供准确的答案。
【解决方案4】:

使用队列(带有单个服务线程)来强制执行排序。您还可以使用任务并行库来管理依赖于其他任务的任务,尽管这对于任意 DB 操作来说非常困难。

我认为您需要重新考虑如何管理传入的操作,以及它们的相互依赖关系是否足够可预测,以至于您可以以这种方式安全地使用多个线程。您可能需要在传入操作中添加一些“取决于”信息以实现该目标。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-01
    • 1970-01-01
    • 2015-02-15
    • 2013-09-02
    • 1970-01-01
    相关资源
    最近更新 更多