首页 > 解决方案 > 池化 SQL 表的 2 个应用程序之间的同步

问题描述

我有 2 个 VB.NET 应用程序实例,每个实例都在自己的专用服务器上运行。所述应用程序在 IDLE 上运行 5s 睡眠的 While true 循环(IDLE 是当表没有任何 ProcessQuery 要处理时)。在每次迭代中,应用程序都会询问 SQL 数据库中的一个表,以了解它是否可以处理任何内容。

问题是我有时会遇到两个实例都“采用”相同 ProcessQuery 的问题。

我正在使用 EntityFramework6。我已经研究过 EntityState,但我认为它并没有完全达到我想要完成的目的。

我想知道拥有完美并行实例的解决方案是什么。在某些时候,我在 12 台机器上运行 12 个实例并非不可能。

谢谢!

 Dim conn As New Info_IndusEntities()
    Dim DemandeWilma As WilmaDemandes =  conn.WilmaDemandes.Where(Function(x) x.Site = 'LONDON' AndAlso x.Statut = 'toProcess').OrderBy(Function(x) x.RequestDate).FirstOrDefault

            If Not IsNothing(DemandeWilma) Then
                DemandeWilma.Statut = Statuts.EnTraitement.ToString
                DemandeWilma.ServerName = Environment.MachineName
                DemandeWilma.ProcessDate = DateTime.Now
                conn.SaveChanges()
                Return DemandeWilma
            end if 

更新(21/06/19)

我发现了一篇我觉得很有趣的文章。

我首先在我的表中添加一列: 在此处输入图像描述

更新(21/06/19)

然后我刷新了我的模型并更改了我的 ORM 中 RowVersion 列的Concurrency Check属性:

在此处输入图像描述

当我测试更新时,这里是 EF6 的日志:

UPDATE [dbo].[WilmaDemandes] SET [Statut] = @0, [ServerName] = @1, [DateDebut] = @2 WHERE (([ID] = @3) AND ([RowVersion] = @4)) SELECT [RowVersion] 来自 [dbo].[WilmaDemandes] 其中@@ROWCOUNT > 0 AND [ID] = @3

-- @0: 'EnTraitement'(类型 = 字符串,大小 = 20)

-- @1: 'TRB5995' (类型 = 字符串,大小 = 20)

-- @2: '2019-06-25 7:31:01 AM' (类型 = DateTime2)

-- @3: '124373' (类型 = Int32)

-- @4: 'System.Byte[]' (类型 = 二进制,大小 = 8)

-- 执行时间:2019-06-25 7:31:24 AM -04:00

-- 在 95 毫秒内完成,结果为:SqlDataReader

2019-06-25 7:31:24 AM -04:00 关闭连接

抛出异常:EntityFramework.dll 中的“System.Data.Entity.Infrastructure.DbUpdateConcurrencyException”

更新(25/06/19)

本文所述,问题始于您使用 DB-First 而不是 Code-First。更新模型后,您的属性将被静默覆盖。当时有些人编写了一个控制台应用程序解决方法,他们在预构建时运行。我不确定我是否准备好将此解决方案作为最终解决方案。

关于如何测试乐观并发以及解决此类异常的方法的有趣教程。

标签: sqlsql-servervb.netentity-framework-6

解决方案


  1. 将“所有者”列添加到您的队列表
  2. 您的应用程序更新了一条记录(TOP 1)并将所有者值设置为其标识符(WHERE Owner IS NULL)
  3. 现在您的应用程序返回并读取它们拥有的行并处理它们

这是一个简单的模式,效果很好。如果任何进程碰巧“同时”取得所有权,那么实际上只有一个进程会获得保留。

我不太擅长 LINQ,所以这里有一个蛮力方法,为了清楚起见多行:

// First try reserving a row
conn.Database.ExecuteSqlCommand(
  "WITH UpdateTop1 AS 
   (SELECT TOP 1 * FROM WilmaDemandes
    WHERE Owner IS NULL 
    AND Site = 'LONDON'
    ORDER BY RequestDate) 

    UPDATE UpdateTop1 SET Owner='ThisApplication'"
);

// See if we got one 
Dim DemandeWilma As WilmaDemandes =  
conn.WilmaDemandes.
Where(x => x.Owner=='ThisApplication').FirstOrDefault

// If we got a row, process it. Otherwise Idle and repeat

您也没有理由必须保留一排。您可以保留所有空闲行并按照自己的方式处理它们。同时,其他进程将拾取任何随后到达的行

就我个人而言,我会重构您的状态列并将其设置为 NULL 以用于准备处理的新记录,否则它是保留它的工作人员 ID。

它还有助于添加时间戳列等内容以记录保留行的时间等。


推荐阅读