【问题标题】:Cache a complex calculation in Rails 3 model在 Rails 3 模型中缓存复杂的计算
【发布时间】:2011-03-05 00:22:31
【问题描述】:

我是 Ruby/Rails 的新手,所以这可能(希望)是一个我不知道答案的简单问题。

我在 Rails 中实现了一个会计/计费系统,我试图在每笔交易之后跟踪运行余额,以便在如下视图中显示它:

日期 说明 费用($) 积分($) 余额($)
3 月 2 日活动 C $4.00 -$7.50
2 月 25 日支付 1 月 $8.00 -$3.50
2 月 23 日活动 B $1.50 -$11.50
2 月 20 日活动 A $2.00 -$10.00

每笔交易(也称为行项目)都存储在数据库中,除了余额之外,上面的所有值(日期、描述、金额)都存储在数据库中。我无法将每笔交易的余额存储在数据库中,因为如果较早的交易发生某些事情(例如后来发布的付款后来失败),它可能会改变。所以我需要为每个行项目即时计算它,并且行项目的余额值取决于它之前的行项目的值(余额 = 上一个行项目的余额 + 这个行项目的金额,即)

所以这是我的问题。我目前(无能)的做法是,在我的 LineItem 模型中,我有一个 balance 方法,它看起来像:

def balance
  prev_balance = 0
  #get previous line items balance if it exists.
  last_line_item = Billing::LineItem.get_last_line_item_for_a_ledger(self.issue_date,self.ledger_item_id)

  if last_line_item
    prev_balance = last_line_item.balance
    .. some other stuff...
 end

 prev_balance + (-1*net_amount) # net_amount is the amount for the current line item
end

这非常昂贵,而且我的视图需要很长时间才能加载,因为我一次又一次地计算上一个订单项的余额。有什么更好的方法来做到这一点?

【问题讨论】:

  • 你能不能不调用第一个 LineItem 的 balance,然后在遍历所有项目时跟踪当前余额?
  • 您是否可以对get_last_line_item_for_a_ledger 进行任何优化,例如急切加载关联?您的数据库字段是否已编入索引?
  • 我有一个像你这样的系统,有交易和余额,不是为了钱,而是为了存货。我将余额直接保存在交易中。每次更新交易时,我都会更新所有下一笔交易,因此当我需要显示余额时,它已经被计算过了。它不是默认实现,但在数据量很大且性能非常重要时会有所帮助。

标签: ruby-on-rails ruby caching activerecord model


【解决方案1】:

您基本上是在为不想在每笔交易中存储余额而付出代价。您可以使用索引优化数据库并使用缓存等;但从根本上讲,如果您有很多交易,您会遇到计算余额需要很长时间的问题。

请记住,您将继续获得新交易,因此您的问题会随着时间的推移而变得更糟。

您可以考虑多种设计方案。首先,就像 Douglas Lise 提到的那样,您可以将余额存储在每笔交易中。如果有较早日期的交易进入,则意味着您可能需要更新自该日期以来的几笔交易。但是,这有一个上限(取决于您希望允许多“旧”事务),因此它具有合理的最坏情况行为。

或者,您可以执行对帐步骤。每个月你都会“关闭”超过 X 周的交易。对帐后,您存储计算的余额。在def balance 中,您现在使用现有的逻辑,但也参考“截至上一次对账的余额”。这再次提供了一个合理且可预测的最坏情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-18
    • 2016-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-25
    • 2014-05-07
    • 2010-09-15
    相关资源
    最近更新 更多