是否有任何准则或经验法则来确定何时存储合计值以及何时动态计算合计值?例如,假设我有一些用户可以评价的小部件(请参见下面的架构)。每次我显示一个小部件时,我都可以从Ratings表中计算平均用户评分。或者,我可以将平均评分存储在Widget表上。这样可以避免我每次显示窗口小部件时都必须计算评分,但是每当用户对窗口小部件进行评分时,我就不得不重新计算平均评分。

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


#1 楼

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

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

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

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

评论


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

–悉达多
17/09/12在2:05



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

– PirateApp
18年6月8日在5:48

#2 楼

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

因此,如果您的网站每天点击量达到1万次,而该网站显示的价值只会发生变化每小时一次,当基础值发生变化时(可能是数据库触发器,无论如何),我都会进行计算。

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

评论


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

– PirateApp
18年6月8日在5:49



@PirateApp,在平均15分钟的窗口中可以看到多少次?您还可以做的是在15分钟的窗口中首次请求时生成该文件,然后将其缓存给不断重复加载的人

–乔
18年6月10日在21:19

它会出现在网站上,所以我认为至少有10000人会看到它,但网站不是实时的,所以没有关于用户行为的实际数据

– PirateApp
18年6月11日在3:02



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

–乔
18年6月11日13:00

#3 楼

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


仅在读取时使用,
在月末使用,
对于某些用户在一天开始时
/> ...


评论


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

–jcolebrand♦
2011年1月6日15:46

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

– Garik
2011年1月7日在17:57



#4 楼

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

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

#5 楼

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

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