Sql heroku上的数据库查询超时

Sql heroku上的数据库查询超时,sql,postgresql,heroku,Sql,Postgresql,Heroku,我正在对一个应用程序进行压力测试,添加一大堆的项目,强迫它做很多工作 select *, ( select price from prices WHERE widget_id = widget.id ORDER BY id DESC LIMIT 1 ) as maxprice FROM widgets ORDER BY created_at DESC LIMIT 20 OFFSET 0 该查询从窗口小部件(约8500个)中选择,其中p

我正在对一个应用程序进行压力测试,添加一大堆的项目,强迫它做很多工作

select *, (
    select price 
    from prices 
    WHERE widget_id = widget.id 
    ORDER BY id DESC
    LIMIT 1
    ) as maxprice
FROM widgets 
ORDER BY created_at DESC 
LIMIT 20 OFFSET 0
  • 该查询从窗口小部件(约8500个)中选择,其中prices有777000个左右的条目
查询在使用基本Heroku共享数据库的测试环境中超时。(使用最大5IG时为193mb)

如何解决超时问题?价格每小时更新一次,因此每小时您将获得850x个新行

对于这个应用程序来说,这是非常多的(实际上它不太可能有8500个小部件),但我想知道什么是解决这个问题的合适方法

我的问题愚蠢吗?(也就是说,执行子选择是一种糟糕的查询方式吗?我的SQL知识很糟糕,这个项目的目标之一就是改进它!)

或者,考虑到价格表的大小,我只是达到了共享数据库的限制,并且应该转移到专用数据库上(例如,Heroku提供的每月最低200美元的专用postgres实例)?在我如何设计DB方面,是否存在更深层次的问题?(也就是说,它是一对多,一个小部件有很多价格。)有没有更明智的方法


我对sql和查询等大规模的世界是完全陌生的,因此我完全不懂上面所说的:)

我不太清楚你在问什么,但我的理解是:

找到您想要定价的小部件。在本例中,看起来您正在查找最近的20个小部件:

SELECT w.id
  FROM widgets
  ORDER BY created_at DESC
  LIMIT 20 OFFSET 0  
对于您找到的20个小部件中的每一个,您似乎希望从小部件表中找到最高的关联价格:

SELECT s.id, MAX(p.price) AS maxWidgetPrice
  FROM (SELECT w.id
          FROM widgets
          ORDER BY created_at DESC
          LIMIT 20 OFFSET 0
        ) s -- widget subset
      , prices p
  WHERE s.id = p.widget_id
  GROUP BY s.id
prices.widget_id需要索引才能生效。如果prices表相对较大,您不希望每次都处理整个prices表,只处理所需行的子集。 编辑:在下面的评论之后添加了“分组依据”(没有,这没有经过测试)

最终版本: @Dave想要每个小部件的
最新价格。您可以在子查询和每个小部件的
限制1
中实现这一点,但在现代PostgreSQL中,窗口函数可以更优雅地完成这项工作。考虑:


每个小部件最高价格的原始帖子:
  • 修复表别名

  • 检索
    小部件的所有列,如问题所示

  • 在PostgreSQL 8.3中,您必须在
    GROUP BY
    子句中拼写出
    SELECT
    列表的所有非聚合列。在PostgreSQL 9.1或更高版本中,主键列将覆盖整个表。我引述该手册:

在主查询时允许查询目标列表中的非GROUP BY列 密钥在GROUPBY子句中指定

  • 我建议不要使用像
    maxWidgetPrice
    这样的工具。在PostgreSQL中,默认情况下,不带引号的标识符折叠为小写。帮自己一个忙,只使用小写标识符

  • 尽可能始终使用显式连接条件。这是规范的SQL方式,更具可读性

  • 偏移量0
    只是噪音


索引: 然而,性能的关键是正确的索引。我将使用两个类似的索引:

CREATE INDEX widgets_created_at_idx ON widgets (created_at DESC);
CREATE INDEX prices_widget_id_idx ON prices(widget_id, price DESC);
第二个是,在您使用第一个索引确定前20个小部件之后,它应该为检索最大奖品提供最佳性能。不确定PostgreSQL 8.3(Heroku shared db的默认版本)是否已经足够智能,可以充分利用它。PostgreSQL 9.1当然是

对于最新价格(参见备注),请使用以下索引:

CREATE INDEX prices_widget_id_idx ON prices(widget_id, created_at DESC);
你不必(也不应该)相信我。使用索引和不使用索引测试性能和查询计划,并亲自查看。索引创建速度应该非常快,即使对于一百万行也是如此


如果您考虑切换到HeloCu上的独立PostgreSQL数据库,您可能会感兴趣:

  • 默认值现在是PostgreSQL 9.1
  • 您现在可以在那里取消长时间运行的查询

  • widget.id
    是主键吗?如果没有,你有索引吗?以及在
    小部件\u id
    ?看起来价格正在更新。如果是这种情况,请在中更新它们,而不是插入。还是你需要保持原来的价格?也不要使用
    select*
    ,因为检索所有列可能会很昂贵。使用
    explain
    了解查询中最昂贵的内容。感谢您提供详细的答案。我得到“列“w.id”必须出现在GROUPBY子句中或用于聚合函数”,如果我在GROUPBY子句中添加w.id,它会要求我将w.title添加到GROUPBY子句中,依此类推,直到添加所有小部件列。那么查询就行了!我以前见过这组人,这让我很困惑。这是9.1中不需要的吗?@dave:是的,我忘记了GROUPBY子句。请看修改后的答案。谢谢,欧文,太好了。我刚刚意识到我需要修改这个查询-getting max(price)从prices表中获取相应小部件的最高价格。我真正需要的是最后的价格。价格通过一个过程在一小时内更新,所以这就是为什么我在最初的查询中在DESC limit 1创建了order by_,但我们走在正确的轨道上,期待着稍后尝试破解这个问题。价格有点像股价,所以每个小部件都有一个随时间而涨跌的股价,所以最后的股价是最重要的,因为这是它的当前价值。@Dave:我为每个小部件添加了一个“最新价格”的解决方案。
    CREATE INDEX widgets_created_at_idx ON widgets (created_at DESC);
    CREATE INDEX prices_widget_id_idx ON prices(widget_id, price DESC);
    
    CREATE INDEX prices_widget_id_idx ON prices(widget_id, created_at DESC);