存储与计算合计值


96

是否有任何准则或经验法则来确定何时存储合计值以及何时动态计算合计值?

例如,假设我有一些用户可以评价的小部件(请参见下面的架构)。每次我显示一个小部件时,我都可以从Ratings表中计算平均用户评分。或者,我可以在Widget表上存储平均评分。这样可以避免我每次显示窗口小部件时都必须计算评分,但是随后,用户每次对窗口小部件进行评分时,我都必须重新计算平均评分。

Ratings       Widgets
---------     -------
widget_id     widget_id
user_id       name              
rating        avg_rating  <--- The column in question

Answers:


58

这取决于。预先计算聚合值会给写入带来更大的负担,派生这些值会使读取更加困难

如果您经常访问派生值,则预先计算是有效的反规范化步骤。但是,在这种情况下,我建议使用实例化视图(写入磁盘的视图,通过触发器链接到父表)。物化视图旨在存储经常问到但繁琐的数据,对于大量写入和少量读取很有用。

在高写入,高读取的情况下,请考虑在后台执行一个任务,该任务模仿实例化视图的效果,但要比实时少。这将显示“足够好”的平均值,同时保留写入和读取性能。

在任何情况下,都不应将派生列视为“常规”列:确保小部件“视图”中显示的数据存在于表中的其他位置,以使整个元组都可以由您采用的任何过程派生。这个问题也是强烈针对数据库(和数据库版本)的,所以我建议针对常规大小的数据集和实例化视图对聚合(使用适当的索引)进行性能测试。


我发现有关物化视图的讨论非常有帮助。它是针对Oracle量身定制的,但可以通俗地理解。对于像我这样来自MySQL背景的人来说,MySQL视图与物化视图不同,它是虚拟的,并且不存储到磁盘(如我在链接中所述)。
Siddhartha

赞!正要问一个确切的问题,我需要存储指标,例如SMA,EMA,WMA,RSI等,它们涉及大量计算,我正在制作一张表格,目前为止我一直在手动刷新,这些指标每次变化100%新数据的到来,有什么好的策略来维护它们,我知道如果每个人都开始左右查询视图,视图将完全破坏数据库
PirateApp

11

与基础数字更改/更新的频率相对应,您需要多久计算/显示一次值。

因此,如果您有一个网站的每日点击量为10k,并且该网站显示的值每小时只会更改一次,那么我会在基础值更改时进行计算(可能是数据库触发器,无论如何)。

如果您有一个工具可以查看统计信息,并且统计信息会在第二秒发生变化,但是您只有三个人可以访问,而且他们一天只看几次,那么我更有可能计算一下它在飞行中。(除非要花上几分钟来计算最初拥有过时的数据并不重要...而且我的老板告诉我每小时都要从cron生成东西,所以他没有等他想看的时候再等。)


每15分钟,有10个指标发生了100%变化,每个指标有1000行
PirateApp

1
@PirateApp,在平均15分钟的窗口中可以看到多少次?什么你也可以做的是产生它首先要求在15分钟的窗口,然后缓存它谁继续把球击了个遍重装的人

它会在网站上显示,所以我认为至少有10,000人会看到它,但网站没有上线,所以没有关于用户行为的实际数据
PirateApp

1
问题是相对于更改频率而言,有多少个请求。因此,如果您在基础数据更改之前预生成了将被查看10,000次的内容,那么可以预生成它。如果只浏览一次或少于一次(因为数据变化如此之快,或者因为很少查看页面,那么您就不会)。

4

将StaleWidgets表用作“无效”(待重新计算)小部件的队列。使用其他线程(异步)任务可以重新计算这些值。重新计算的时间或片刻取决于系统要求:

  • 刚读,
  • 在月底,
  • 对于一天开始的某些用户
  • ...

1
那么他们如何进入陈旧的队列呢?
jcolebrand

2
@jcolebrand ..正在为某些小部件插入/删除评分(评分表)时。这时Widgets表中的平均值变得无效,因此我们必须在表中插入StaleWidgets记录,该记录只有一列-widget_id。使用触发器或存储的proc,它将记录插入到Ratings表或您的变体中。
加里克2011年

2

我建议如果计算不太麻烦,并且要进行复杂的计算和频繁的更新,但不能读取频率信息,则可以进行实时计算,因为它可以存储计算的数据并有多余的column(bool)来存储是否需要重新计算。例如,每当应进行重新计算但不进行重新计算时将此列设置为true,并且在进行重新计算时将此列设置为false(这将表示计算的值是最新的而不是过时的)。

这样,您不必每次都重新计算,仅在必须读取且重新计算列的值为true时才进行计算。这样,您将节省大量的重新计算。


2

特别是对于这种情况,有一个不同的解决方案,您不必将所有评级相加并除以总数即可得出平均值。取而代之的是,您可以拥有另一个包含评论总数的字段,因此,每次添加评分时,您都使用(avg_rating×total + new_rating)/ total计算新的平均值,这比聚合要快得多,并且减少了磁盘读数,因为您不必访问所有额定值。类似的解决方案可能适用于其他情况。

不利的一面是,这不是一项艰巨的交易,因此您可能会以过时的评级结束。但是您仍然可以通过使用数据库中的触发器来解决该问题。另一个问题是数据库不再进行规范化,但不要害怕对数据进行规范化以换取性能。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.