数据库查询在heroku上超时

时间:2012-03-08 21:39:52

标签: 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),价格中包含777000个条目。

查询在使用基本Heroku共享数据库的测试环境中超时。 (使用最大5g的193mb)

什么能解决这个超时问题?价格每小时更新一次,因此您每小时可获得8500x新行。

这个应用程序的数量非常多(实际上它不太可能有8500个小部件),但我想知道什么是合适的解决方案?

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

或者我只是达到了共享数据库的限制,并且应该在价格表的大小下进入专用数据库(例如Heroku的每月最低200美元专用postgres实例)。就我如何设计数据库而言,是否存在更深层次的问题? (即它是一对多,一个小部件有很多价格。)有更明智的方法吗?

我对sql和查询等大规模的世界全新,因此上面表达的完全无知。 :)

2 个答案:

答案 0 :(得分:1)

以下评论后的最终版本:

@Dave想要每个小部件latest price。您可以在子查询和每个小部件LIMIT 1中执行此操作,但在现代PostgreSQL中,窗口函数可以更优雅地完成工作。考虑first_value() / last_value()

SELECT w.*
     , first_value(p.price) OVER (PARTITION BY w.id
                                  ORDER BY created_at DESC) AS latest_price
FROM (
    SELECT *
    FROM   widgets
    ORDER  BY created_at DESC
    LIMIT  20
    )  w
JOIN   prices p ON p.widget_id = w.id
GROUP  BY w.col1, w.col2 -- spell out all columns of w.*

每个小部件的最高价格的原始帖子:

SELECT w.*
     , max(p.price) AS max_price
FROM (
    SELECT *
    FROM   widgets
    ORDER  BY created_at DESC
    LIMIT  20
    )  w
JOIN   prices p ON p.widget_id = w.id
GROUP  BY w.col1, w.col2 -- spell out all columns of w.*
  • 修复表别名。

  • 检索widgets的所有列,例如问题演示

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

  

在主要时,允许查询目标列表中的非GROUP BY列   key在GROUP BY子句中指定

  • 我建议永远不要使用像maxWidgetPrice这样的mixed case identifiers。 PostgreSQL默认情况下将不带引号的标识符折叠为小写。帮自己一个忙,并专门使用小写标识符。

  • 始终在可能的情况下使用显式JOIN条件。这是规范的SQL方式,而且更具可读性。

  • OFFSET 0只是噪音


索引:

然而,表现的关键是正确的索引。我会去两个这样的索引:

CREATE INDEX widgets_created_at_idx ON widgets (created_at DESC);
CREATE INDEX prices_widget_id_idx ON prices(widget_id, price DESC);

第二个是multicolumn index,在使用第一个索引确定前20个小部件后,应该为检索最大奖励提供最佳性能。不确定PostgreSQL 8.3(Heroku共享数据库的默认值)是否足够智能以充分利用它。 PostgreSQL 9.1肯定是。

对于最新价格(请参阅评论),请改用此索引:

CREATE INDEX prices_widget_id_idx ON prices(widget_id, created_at DESC);

你不必(也不应该)只相信我。使用带有和不带索引的EXPLAIN ANALYZE测试性能和查询计划,并亲自查看。索引创建应该非常快,即使是一百万行。


如果您考虑在Heroku上切换到独立的PostgreSQL数据库,您可能会对this recent Heroku blog post感兴趣:

  1. 默认为PostgreSQL 9.1。
  2. 您可以立即取消长时间运行的查询。

答案 1 :(得分:0)

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

找到您想要定价的小部件。在这种情况下,您似乎正在寻找最近的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
需要对price.widget_id建立索引才能使其生效。如果它相对较大,您不希望每次都处理整个价格表,只需要您需要的行子集。 编辑:添加“分组依据”(并且没有,这没有经过测试)