如何提高SQL中普通方法的性能?

时间:2010-12-15 12:03:25

标签: sql performance postgresql

我遇到了一些性能问题,其中计算列平均值的SQL查询随着记录数量的增长而逐渐变慢。是否有可以添加到列中的索引类型,以便更快地进行平均计算?

有问题的数据库是PostgreSQL,我知道特定的索引类型可能不可用,但我也对理论答案很感兴趣,如果没有某种缓存解决方案,这甚至是天气。

更具体地说,有问题的数据本质上是一个具有这种定义的日志:

table log {
  int duration
  date time
  string event
}

我在做像

这样的查询
SELECT average(duration) FROM log WHERE event = 'finished'; # gets average time to completion
SELECT average(duration) FROM log WHERE event = 'finished' and date > $yesterday; # average today

第二个总是相当快,因为​​它具有更严格的WHERE子句,但总平均持续时间1是导致问题的查询类型。我知道我可以使用OLAP或其他东西来缓存这些值,我的问题是天气,我可以通过数据库方面的优化(如索引)完全实现这一点。

5 个答案:

答案 0 :(得分:6)

计算平均值的性能总是会越慢,记录越多,总是必须使用结果中每条记录的值。

如果索引包含的数据少于表本身,则索引仍然可以提供帮助。为您想要平均值的字段创建索引通常没有帮助,因为您不想进行查找,您只想尽可能高效地获取所有数据。通常,您会将该字段作为输出字段添加到查询已使用的索引中。

答案 1 :(得分:2)

取决于你在做什么?如果您没有过滤数据,那么除了按顺序拥有聚簇索引之外,数据库还有什么方法来计算列的平均值?

有些系统执行在线分析处理(OLAP),可以执行诸如保持运行总和以及平均下来要检查的信息等操作。这一切都取决于你正在做什么以及你对“慢”的定义。

如果你有一个基于网络的程序,也许你可以每分钟生成一次平均值然后缓存它,一遍又一遍地向用户提供缓存的值。

答案 2 :(得分:2)

加速聚合通常是通过保留其他表来完成的。

如果您希望使AVG(或其他聚合函数)的性能几乎保持不变,无论记录数量是多少都可以引入新表,那么假设表detail(id, dimA, dimB, dimC, value)相当大

dimAavg(dimA, avgValue)

  • 此表的大小仅取决于dimA的不同值的数量(此外,此表在您的设计中可能有意义,因为它可以详细保存dimA可用值的域(以及与此相关的其他属性)域值;您可能/应该已经有这样的表)
  • 此表仅在您仅按dimA进行anlayze时才有用,一旦您根据dimA需要AVG(值)并且dimB变得无用。因此,您需要知道要对哪些属性进行快速分析。在多个属性上保留聚合所需的行数为n(dimA) x n(dimB) x n(dimC) x ...,这可能会或可能不会很快增长。
  • 维护此表会增加更新的成本(包括插入和删除),但您可以采用进一步的优化...

例如,让我们假设系统主要进行插入,偶尔更新和删除。

进一步假设您只想通过dimA进行分析,并且id正在增加。然后有

等结构
dimA_agg(dimA, Total, Count, LastID) 

可以在不对系统产生重大影响的情况下提供帮助。

这是因为你可能有触发器不会在每个插入物上触发,但是可以说在100个插入物上。

通过这种方式,您仍然可以从 这个详细信息表中获取准确的聚合

SELECT a.dimA, (SUM(d.value)+MAX(a.Total))/(COUNT(d.id)+MAX(a.Count)) as avgDimA
FROM details d INNER JOIN
     dimA_agg a ON a.dimA = d.dimA AND d.id > a.LastID 
GROUP BY a.dimA

上面带有正确索引的查询将从dimA_agg获得一行,而detail只有少于100行 - 这将在接近恒定的时间内执行(~log fanout n)并且不需要为每个插入更新dimA_agg(减少更新惩罚)。

100的值仅作为示例给出,您应该自己找到最佳值(或者甚至保持变量,但在这种情况下触发器仅仅是不够的。)

维护删除和更新必须触发每个操作,但您仍然可以检查要删除或更新的记录的ID是否已经在统计信息中,以避免不必要的更新(将节省一些I / O)。 / p>

注意:对具有谨慎属性的域进行分析;在处理时间序列时,情况变得更加复杂 - 您必须确定要保留摘要的域的粒度。

修改

还有materialized views23

答案 3 :(得分:0)

只是一个猜测,但索引不会有多大帮助,因为平均值必须读取所有记录(按任何顺序),索引对行的查找子集很有用,如果你必须迭代所有行而没有特殊的排序索引没有帮助...

答案 4 :(得分:0)

这可能不是您正在寻找的,但如果您的表有某种方式来订购数据(例如按日期),那么您可以只进行增量计算并存储结果。

例如,如果您的数据有一个日期列,您可以计算记录1 - Date1的平均值,然后存储该批次的平均值以及Date1和您平均的#records。下次计算时,将查询限制为结果Date1..Date2,并添加记录数,并更新查询的最后日期。您拥有计算新平均值所需的所有信息。

执行此操作时,显示日期索引或您用于订购的任何列显然会有所帮助。