在数据库中缓存计算值(总和/总计)

Posted

技术标签:

【中文标题】在数据库中缓存计算值(总和/总计)【英文标题】:Caching calculated values (Sums/Totals) in the Database 【发布时间】:2009-08-26 22:45:01 【问题描述】:

考虑以下对象模型(->> 表示集合):

客户->订单

Orders->>OrderLineItems->ProductPrice

该应用专注于处理订单,因此在 UI 中使用了显示所有符合特定条件的所有订单的大多数时间表。 99% 的时间我只对显示 LineTotals 的总和感兴趣,而不是单个 LineTotals。

再想一想,每个订单也可能有多次付款(电汇、支票、信用卡等),同样,我只对我收到的金额感兴趣。

在查询数据库中的订单时,我不想选择所有订单,然后为每个订单选择其付款和 LineItems。

我的想法是将每个订单与“状态”对象关联起来,缓存订单的所有总和和状态,以数量级提高查询性能,还支持未付款订单、已付款订单、到期订单的查询场景等等

这可以防止域逻辑(例如,当订单被视为已付款时)泄漏到数据库查询中。但是,它有责任使总和保持最新。系统通常有明确定义的点,需要发生的地方,例如。输入或整合付款,创建/修改订单。

到目前为止,我使用了 Observable Collections,当添加或删除项目或更新项目的某些属性时,它会触发重新计算状态。我问自己,从 ddd 的角度来看,所有这些的逻辑应该放在哪里。在聚合根中强制所有事件连接和计算逻辑对我来说似乎很奇怪。

【问题讨论】:

这个问题就这么无趣吗? 我觉得很有趣。不知道我会如何处理它。 【参考方案1】:

您需要在意图揭示界面中表达请求的意图,以便您的存储库能够准确了解您想要做什么并做出相应的反应。在这种情况下,界面不是向其他开发人员而是向其他代码显示意图。因此,如果您想要状态或总数,请创建一个显示此意图的界面并从您的存储库中请求该类型的对象。然后,存储库可以创建并返回一个域对象,该对象封装了计算总数所需的工作,仅此而已。

此外,您的 DAL 可以智能地从您请求的接口中选择要应用的获取策略,即在您不需要访问子对象的情况下延迟加载和在您需要访问的情况下进行预加载。

Udi Dahan 有一些great blog posts about this。他撰写并谈到了将意图揭示接口应用于这个问题,他称之为making roles explicit。

【讨论】:

问题不仅在于在哪里计算总数,还在于如何计算,因为当我想计算总数时,我需要所有数据,这就是为什么我考虑在数据库中“缓存”它的原因跨度> 这就是我的意思——你的意图要求你需要所有的数据,所以你必须给你的数据访问技术这个“提示”。通过在界面中明确角色,您的 DAL 可以选择适当的获取策略,无论是懒惰的还是急切的 - 但我在重复自己 - 这就是我在回答中所说的。我不知道你为什么认为它没有回答这个问题:-( 我不想一次又一次地计算数据,因为这意味着我必须从数据库中加载所有数据,这是我不想要的,因为它非常严重地损害了性能!我希望数据库中的数据(计算总和)有点“缓存”,所以我可以将它们抓取用于只读目的。我的问题是如何保持缓存有效/最新。 啊,对不起约翰内斯,我的错误。我建议你看一下域事件来处理这个(udidahan.com/2009/06/14/domain-events-salvation)。例如,每当发生会更新总数的事情时,您都会引发域事件。您可以在域事件中传递增量并将其添加到总数中,而无需重新计算。 看起来不错。请编辑您的帖子/使其成为社区 wiki,我会这样做并标记为答案。【参考方案2】:

我强烈建议研究支持 LINQ 的 OR(对象关系)映射器。命名两个主要的,LINQ to SQL 和 Entity Framework,都来自 Microsoft。我相信 LLBLGen 现在也支持 LINQ,并且 nHibernate 有一些您可以尝试的半生不熟的 LINQ 解决方案。我的主要推荐是 Entity Framework v4.0,它可通过 .NET 4.0 测试版或 Visual Studio 2010 测试版获得。

通过启用 LINQ 的 OR 映射器,您可以轻松地动态查询所需的聚合信息,实时,仅使用您的域模型。业务逻辑无需泄漏到数据层,因为您通常不会使用存储过程。 OR 映射器即时为您生成参数化 SQL。 LINQ 与 OR 映射器相结合是一个非常强大的工具,它不仅允许您查询和检索实体和实体图,还可以查询域模型上的数据投影……允许检索自定义数据集、聚合等。通过单一的概念模型。

【讨论】:

我已经使用 OR 映射器 (LinqToSql) 并且我已经使用标准组合和预测。这是关于数据库性能的,每个视图我必须查询大约 10000 个订单。有点像 (Select N)+1 Problem【参考方案3】:

“在聚合根中强制所有事件接线和计算逻辑对我来说似乎很奇怪。”

这通常是对“服务”的调用。

【讨论】:

以上是关于在数据库中缓存计算值(总和/总计)的主要内容,如果未能解决你的问题,请参考以下文章

计算列的总和(总计)并在 PHP 中按日期搜索

MongoDB 聚合:从先前行的总和计算运行总计

excel如何计算一列数字的总和?

c#怎么计算Datatable里面数据的合计和总计

ruby 练习:计算数组totalWrite一个方法总计,它将一个数字数组作为输入并返回它们的总和(总和)。

在 Shiny 中将总计/小计添加到数据表的底部