【问题标题】:Mid-Tier Help Needed需要中层帮助
【发布时间】:2019-06-07 00:05:59
【问题描述】:

一句话,我最终需要知道的是如何在中间层函数之间共享对象而不需要应用层传递数据模型对象。

我正在为我工​​作的公司在当前环境中构建一个中间层。目前,我们主要使用 .NET 进行编程,并围绕我们所有的各种数据库系统(包括 Oracle、OpenLDAP、MSSQL 等)构建了自定义数据模型。

我在尝试从应用层拉取我们的模型并将其移动到一系列中间层库时遇到了问题。我遇到的主要问题是应用程序层能够在整个进程期间挂起缓存的对象并根据缓存的数据进行更新,但中间层操作没有。

我试图将模型对象尽可能地排除在应用程序之外,这样当我们对底层数据库结构进行更改时,我们可以轻松地编辑和重新部署中间层,并且多个应用程序不需要被重建。我将简要介绍一下伪代码的问题所在,因为这是我们开发人员最了解的内容:)

main
 {
  MidTierServices.UpdateCustomerName("testaccount", "John", "Smith");

  // since the data takes up to 4 seconds to be replicated from
  // write server to read server, the function below is going to
  // grab old data that does not contain the first name and last
  // name update....  John Smith will be overwritten w/ previous
  // data

  MidTierServices.UpdateCustomerPassword("testaccount", "jfjfjkeijfej");
 }

 MidTierServices
 {
  void UpdateCustomerName(string username, string first, string last)
  {
   Customer custObj = DataRepository.GetCustomer(username);

   /*******************
   validation checks and business logic go here...
   *******************/

   custObj.FirstName = first;
   custObj.LastName = last;

   DataRepository.Update(custObj);
  }

  void UpdateCustomerPassword(string username, string password)
  {
   // does not contain first and last updates
   Customer custObj = DataRepository.GetCustomer(username); 

   /*******************
   validation checks and business logic go here...
   *******************/

   custObj.Password = password;

   // overwrites changes made by other functions since data is stale
   DataRepository.Update(custObj); 
  }
 }

另一方面,我考虑过的选项是构建一个本地缓存层,这需要大量时间,而且是一个很难向管理层出售的概念。使用内置缓存支持的不同建模层,例如 nHibernate:这也很难卖给管理层,因为这个选项也需要很长时间才能拆开我们的整个自定义模型并用第三方解决方案替换它.此外,支持我们大量数据库的供应商并不多。例如,.NET 有 LINQ to ActiveDirectory,但没有 LINQ to OpenLDAP。

不管怎样,很抱歉这篇小说,但这更像是一个企业架构类型的问题,而不是一个简单的代码问题,例如“如何在 .NET 中获取当前日期和时间?”

编辑

抱歉,我忘记在我的原始帖子中添加一些非常重要的信息。我感觉很糟糕,因为 Cheeso 遇到了很多麻烦来写一个非常深入的回复,如果没有更多的问题(我愚蠢地没有包括),这将解决我的问题。

我面临当前问题的主要原因是数据复制。第一个函数写入一个服务器,然后下一个函数从另一个尚未接收到复制数据的服务器读取。所以本质上,我的代码比数据复制过程更快。

我可以通过始终读取和写入同一个 LDAP 服务器来解决此问题,但我的管理员可能会因此而谋杀我。专门设置了一个仅用于写入的服务器,然后在负载均衡器后面设置了 4 个其他服务器,这些服务器仅用于读取。我绝不是 LDAP 管理员,所以我不知道这是否是标准程序。

【问题讨论】:

    标签: c# architecture caching soa


    【解决方案1】:

    您正在描述一个非常常见的问题。

    解决这个问题的正常方法是使用Optimistic Concurrency Control

    如果这听起来像 gobbledegook,那不是。这是很简单的想法。该术语的并发部分是指记录数据发生更新,并且这些更新同时发生的事实。可能很多作家。 (您的情况是一个退化的情况,其中一个作家是问题的根源,但这是相同的基本思想)。我马上就会谈到乐观的部分。

    问题

    当有多个写入器时,两个更新的读+写部分可能会交错。假设您有 A 和 B,他们都读取并更新数据库中的同一行。 A 读取数据库,然后 B 读取数据库,然后 B 更新它,然后 A 更新它。如果你有一个幼稚的方法,那么“最后一次写入”将获胜,而 B 的写入可能会被破坏。

    输入乐观并发。基本思想是假设更新将起作用,但请检查。有点像几年前信任但验证的军备控制方法。这样做的方法是在数据库表中包含一个字段,该字段也必须包含在域对象中,它提供了一种区分数据库行或域对象的一个​​“版本”的方法。最简单的方法是使用一个名为 lastUpdate 的时间戳字段,它保存上次更新的时间。还有其他更复杂的方法来进行一致性检查,但时间戳字段可用于说明目的。

    然后,当编写者或更新者想要更新数据库时,它只能更新键匹配的行(无论您的键是什么)以及当 lastUpdate 匹配时。这是验证部分。

    由于开发人员了解代码,我将提供一些伪 SQL。假设您有一个博客数据库,其中包含每个博客条目的索引、标题和一些文本。您可以像这样检索一组行(或对象)的数据:

    SELECT ix, Created, LastUpdated, Headline, Dept FROM blogposts 
        WHERE CONVERT(Char(10),Created,102) = @targdate 
    

    这种查询可能会检索数据库中给定日期、月份或其他任何时间的所有博客文章。

    使用简单的乐观并发,您可以像这样使用 SQL 更新单行

    UPDATE blogposts Set Headline = @NewHeadline, LastUpdated = @NewLastUpdated
        WHERE ix=@ix AND LastUpdated = @PriorLastUpdated
    

    只有当索引匹配时才会发生更新(我们假设这是主键),并且 LastUpdated 字段与读取数据时的字段相同。另请注意,您必须确保每次更新行时都更新 LastUpdated 字段。

    更严格的更新可能会坚持没有更新任何列。在这种情况下,根本没有时间戳。像这样的:

    UPDATE Table1 Set Col1 = @NewCol1Value,
                  Set Col2 = @NewCol2Value,
                  Set Col3 = @NewCol3Value
    WHERE Col1 = @OldCol1Value AND
          Col2 = @OldCol2Value AND
          Col3 = @OldCol3Value
    

    为什么叫“乐观”?

    OCC 被用作保持数据库锁的替代方法,这是一种保持数据一致性的强硬方法。数据库锁可能会阻止 任何人 在数据库行被持有时读取或更新它。这显然具有巨大的性能影响。因此 OCC 放松了这一点,并“乐观地”采取行动,假设当需要更新时,表中的数据不会同时更新。但这当然不是盲目的乐观——你必须在更新前检查一下。

    在实践中使用 Optimistic Cancurrency

    你说你使用.NET。我不知道您是否使用 DataSets 进行数据访问,强类型或其他方式。但是 .NET 数据集,或者特别是 DataAdapter,包括对 OCC 的内置支持。您可以为任何 DataAdapter 指定并手动编码 UpdateCommand,您可以在此处插入一致性检查。这也是possible within the Visual Studio design experience


    (来源:asp.net

    如果您遇到违规行为,更新将返回显示零行已更新的结果。您可以在DataAdapter.RowUpdated event 中查看。 (请注意,在 ADO.NET 模型中,每种数据库都有不同的 DataAdapter。SqlDataAdapter 的链接适用于 SQL Server,但对于不同的数据源,您需要不同的 DA。)

    在 RowUpdated 事件中,您可以检查受影响的行数,然后在计数为零时采取一些措施。


    总结

    在写入更新之前验证数据库的内容没有被改变。这称为乐观并发控制


    其他链接:

    【讨论】:

    • 哇!!非常感谢芝士!这绝对是我收到的最彻底的回应。我希望我能给你 500 分,因为你在这个回复中投入的时间。不幸的是,OCC 无法解决我的问题。问题是即使在我写入数据的那一刻,更新的时间戳仍然没有被复制到读取服务器。因此,如果我要再次提取数据并验证,时间戳仍然没有改变,因为它最初被读取。我面临的是一场与复制的时间战。我的代码比复制过程更快。
    • 我明白你在说什么,但我认为你是不正确的。更新的时间戳不会复制到 READ 服务器,我得到了那部分。但是当您执行UPDATE Set Col=@col WHERE LastUpdated=@PriorLastUpdated 时,您正在直接从正在更新的服务器进行读取检查。我了解代码与复制的竞赛;即便如此,使用 OCC 检查的更新将防止数据被清除。但它并不能完全解决您的问题 - 您仍然必须弄清楚在 OCC 检查失败的情况下该怎么做。该错误处理是特定于应用程序的。
    • 好的,现在我和你在一起。我多次阅读您的帖子,但从未以这种方式阅读过。有时我有点慢。唯一的问题是我真的不知道使用 LDAP 的效果如何。我不相信他们支持 where 子句。据我所知,我只能在目录中设置或获取单个属性。然而,这是一个完全不同的问题。你肯定回答了最初的问题,非常好。再次感谢!!
    猜你喜欢
    • 2011-09-09
    • 1970-01-01
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 2022-01-04
    • 2017-10-05
    • 2017-03-23
    • 2012-09-26
    相关资源
    最近更新 更多