【问题标题】:How to synchronize database access in a WCF Service?如何在 WCF 服务中同步数据库访问?
【发布时间】:2012-04-15 22:07:29
【问题描述】:

我以前使用过 WCF 服务,现在我有一个新项目即将推出。 我仍处于设计阶段,我想知道处理以下场景的最佳方法是什么。

我将有多个客户端同时连接到我的 WCF 服务,在服务上触发不同的方法(操作合同):

A. 一些触发的方法只是纯粹的“读取”方法(例如 GetListOfCustomers)。

B. 触发的一些方法是复杂的“读取”方法(例如 GetAllProductsByCustomerId)。 这些方法需要从数据库中获取客户, 检查他的东西,然后得到他购买的所有产品。 (意思是,在这个方法中有 2 次对数据库的调用)。

C.有些是“Write”方法(例如“RemoveCustomer”或“SetProductOutOfStock”)。

我的问题是 - 如何同步所有这些调用,以免出现并发问题?

希望整个服务串行处理调用,因为这会损害客户端的性能(某些调用可能需要 3-4 秒才能处理)。 那么我的解决方案是什么?

为所有具有“多个”线程的客户端使用“单个”实例,然后使用锁定对象?这不会导致连续吗?

或者我是否需要一个不同的“读”锁定对象和一个不同的“写”锁定对象?

或者我是否需要为“写”功能加锁而为“读”功能加锁?

这是我在 StackOverflow 上的第一个问题。 感谢任何可以提供帮助的人!

更新:我将使用“Linq-To-SQL”作为我的 ORM。

【问题讨论】:

  • 请考虑将其中一个答案标记为已接受。

标签: database wcf concurrency


【解决方案1】:

您不必担心数据一致性问题,也不必担心执行数据库查询时的并发性。如果我正确理解了您的情况,您需要确保的是在执行一系列您希望“原子”的数据库查询时始终如一地使用事务。我将尝试用一个例子来解释它:

  1. 从数据库中获取所有客户。
  2. 对于每个客户,执行 查询更新一些相关数据。

在这种情况下您不希望发生的情况是,当来自1 的查询返回之后之前 时另一个查询更改了数据> 来自2 的所有查询都已完成。例如,如果其中一位客户同时被删除,则更新相关数据毫无意义 - 甚至可能导致一些错误。

所以你需要做的就是在1 之前加上BEGIN TRANSACTIONCOMMIT2 之后。查找您正在使用的 SQL 方言的确切语法。

基本上,这将确保您正在使用的数据不会改变。事实上,它会被你的交易锁定;并且所有其他可能使用相同数据的查询都在等待您的交易完成。数据库智能地执行这种锁定,总是试图锁定尽可能少的数据。

【讨论】:

  • 感谢您的快速回复。 1. 我正在使用 Linq-To-SQL。你写道我应该使用'BEGIN TRANSACTION'和'COMMIT',但那是SQL。我是否需要使用末尾带有“Commit”的“TransactionScope”对象? 2. 我只需要在“写”功能中使用“交易”吗?还是我在“读取”功能中也需要它? 3. 如何防止两个不同的调用同时更新相同的数据?如果我有一个客户——而且只有一位经理可以“承担责任”。如何防止它们相互“覆盖”?
  • 首先,Linq-To-Sql 将 LINQ 查询转换为真正的 SQL,因此,如果您了解事务如何在底层工作,您将轻松地将您的知识映射到 LiNQ 概念上。请先阅读对this question 的回复。如果您在阅读完此类文章后还有任何问题,我很乐意为您解答。
  • 谢谢。我已阅读您发布的链接,以及 Scott Guthrie here 的文章。但他们似乎写了一些令人困惑的东西:那里的一位 cmets 写道“LINQ to SQL 似乎在提交时为你解决了所有依赖项”。另一位写道“db DataContext 初始化和 db.SubmitChanges() 之间的所有操作都由 .Net 围绕数据库事务进行包装,以确保您的数据库保持一致并在表之间保持适当的完整性”。
  • 另一方面 - 在链接中我看到提到“使用乐观并发处理同时更改”。那么……真实情况如何?我是否需要在我的 WCF 服务中防止并发?
【解决方案2】:

您正在为 Web 设计,因此您需要采用并发性,没有出路。在我看来,获取锁并不是在网络上工作的好方法。 并发访问即将发生,您需要考虑如何处理并发错误而不是防止并发问题。任何基于 Web 构建的并发控制机制都是有限的,并且很难正确构建。

【讨论】:

    【解决方案3】:

    我建议您阅读有关 CQRS 的内容,这是一种架构模式,可以在某种程度上解决您面临的挑战。

    作为您的情况的示例解决方案,下图是可以满足您要求的 CQRS 架构。

    如果您需要进一步的解释,我很乐意提供。

    更新

    主要原则是将数据库读取和写入分开到不同的数据库中。

    然后您使用复制来确保您的读取数据库是最新的。

    您还可以通过将两个数据库合二为一,将两个服务合二为一来实现上述架构。但是,您最初的问题是基于您的数据库使用模式存在冲突这一事实而引起的数据库争用。

    这就是我提出 CQRS 作为解决方案的原因 - 数据库的写入端与读取端分离。读取端可以针对选择进行优化(甚至可以针对访问速度进行非规范化)。

    这意味着您将不会遇到与通过同一接口同时执行读取和写入操作时相同的争用问题 - 这是您当前的方法。

    此外,您不需要通过服务公开您的读取操作 - 简单的 ADO 就可以了,服务端点只会引入延迟。

    当您从读取模型读取数据以及数据库写入服务更新数据模型时,您仍然可以使用 linq2sql。

    【讨论】:

    • 嗯...不确定我是否理解这一点。我将使用“Linq-To-SQL”
    • 我知道你在那里做了什么,但是如果没有具体的例子,我很难理解它。你有什么要给我看的吗?
    • @JohnMiner 看看this article,它以直截了当的方式解释了 CQRS 的基本概念。
    • -1 用于 CQRS,请先在实际项目中亲自尝试,然后再推荐。对于每小时阅读量少于 100 万且具有 5 年架构师经验的项目,我不会推荐 CQRS。事实上,我不会为任何实际项目推荐 CQRS。
    • @AlexBurtsev,感谢您的诚实评估-如果我被证明是错误的,我很开放,可以改变我的想法,但是您没有提供任何有效的分歧,除非您不会推荐它。请提供一份。我目前正在实现与上述类似的 CQRS 类型设计。此外,我不同意您需要 5 年的架构师才能理解它。一个
    【解决方案4】:

    我的问题是 - 我如何同步所有这些调用,这样我就不会 遇到并发问题?

    为什么需要同步任何东西? DBMS 可以很好地处理同步。当然,如果你知道你会有很多写操作会因为锁而降低你的读取性能,那么你将不得不在架构级别上对此进行规划,但这并没有什么可做的。用 WCF 做。在这种情况下,正如休所写的那样,CQRS 可能是一个合适的选择,尽管没有手头的规范很难说。

    我不希望整个服务串行处理调用,因为它 会损害客户端的性能(某些调用可能需要 3-4 秒处理)。那么我的解决方案是什么?

    如果可以的话,然后使用 PerCall 实例化和单并发或多并发。查看here 了解有关实例化/并发组合的详细信息。

    为所有具有“多个”线程的客户端使用“单个”实例,并且 然后使用锁定对象?这不会导致连续吗?

    单实例化会给您带来并发问题,请参阅我上面链接的文章。如果您需要 Singleton 服务,它最有用。


    更新

    在回答您的评论时:恐怕我仍然不明白您希望在哪里遇到并发问题。如果您担心 WCF 服务,请避免使用 Single instancing:最具可扩展性的选项是 PerCall/Multiple。

    如果您问自己是否需要考虑 CQRS,请随时阅读我在另一条评论中链接的 article by Udi Dahan。但请记住,CQRS 需要一些时间来理解并增加您可能需要或不需要的项目的复杂性。

    我建议不要无用地过度复杂化您的架构,在我看来,为您的服务正确配置实例化/并发性就足够了。如果您不确定,请编写一些原型来伪造系统的预期负载。比抱歉更安全,特别是如果它是一个长期存在的应用程序。

    【讨论】:

    • 我的场景是这样的:对服务的大部分调用将是“读取”调用(80%),其余(20%)是“写入+读取”调用(意思是更新所有昨天进来的顾客)。此外 - WCF 服务上还有另一个线程将启动向客户端发送信息(例如来自已到达新客户的数据库的通知)。因此,鉴于此 - 你有什么建议?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-17
    相关资源
    最近更新 更多