5

我需要runnerId.

这个查询:

SELECT "runnerId" FROM betlog WHERE "marketId" = '107416794' ;

需要 80 毫秒(1968 个结果行)。

这个:

SELECT min("runnerId") FROM betlog WHERE "marketId" = '107416794' ;

需要 1600 毫秒。

有没有更快的方法来找到最小值,或者我应该在我的 java 程序中计算最小值?

"Result  (cost=100.88..100.89 rows=1 width=0)"
"  InitPlan 1 (returns $0)"
"    ->  Limit  (cost=0.00..100.88 rows=1 width=9)"
"          ->  Index Scan using runneridindex on betlog  (cost=0.00..410066.33 rows=4065 width=9)"
"                Index Cond: ("runnerId" IS NOT NULL)"
"                Filter: ("marketId" = 107416794::bigint)"

CREATE INDEX marketidindex
  ON betlog
  USING btree
  ("marketId" COLLATE pg_catalog."default");

另一个想法:

SELECT "runnerId" FROM betlog WHERE "marketId" = '107416794' ORDER BY "runnerId" LIMIT 1 >1600ms
SELECT "runnerId" FROM betlog WHERE "marketId" = '107416794' ORDER BY "runnerId" >>100ms

如何LIMIT减慢查询速度?

4

3 回答 3

8

您需要的是一个多列索引

CREATE INDEX betlog_mult_idx ON betlog ("marketId", "runnerId");

如果有兴趣,您可以在dba.SE 上的相关问题下找到有关 PostgreSQL 中的多列索引、链接和基准的深入信息。

我是怎么想出来的?
在多列索引中,行按索引的第一列(“marketId”)排序(从而聚集),每个簇依次按索引的第二列排序 - 因此第一行符合条件min("runnerId"). 这使得索引扫描非常快。

关于LIMIT减慢查询速度的悖论效应——Postgres 查询规划器有一个弱点。常见的解决方法是使用 CTE(在这种情况下不是必需的)。在这个最近的密切相关的问题下找到更多信息:
PostgreSQL querytaking too long

于 2012-11-24T22:47:25.807 回答
1

当您有一个索引("runnerId")(或至少"runnerId"作为高阶列)但没有索引时("marketId", "runnerId"),比较使用该列上的索引传递所有行与匹配项的成本,并从该组中"marketId"挑选出最小值使用索引扫描并在找到匹配的第一行时停止"runnerId"的成本。基于可用的统计数据以及值将随机分布在其上的索引的索引条目中的假设,估计后一种方法的成本较低。"runnerId""marketId""marketId""runnerId"

它还估计了扫描整个表并从匹配行中挑选最小值以及可能还有许多其他替代方案的成本。它并不总是使用某种类型的计划,而是比较所有替代方案的成本。

问题是值将随机分布在范围内的假设不一定正确(如本例所示),导致扫描范围的高百分比以查找潜伏在末尾的行。对于 的某些值"marketId",所选值在索引开头附近可用"runnerId",此计划应该非常快。

PostgreSQL 开发人员社区已经讨论过,如果数据分布不是假设的那样,我们可能会如何偏向运行时间较长的“风险”计划,并且已经在跟踪多列统计数据以便相关价值观不会遇到这样的问题。预计在接下来的几个版本中会在这方面有所改进。在那之前,Erwin 的建议都是针对如何解决这个问题的。

基本上,它归结为提供更有吸引力的计划或引入优化障碍。在这种情况下,您可以通过添加索引来提供更有吸引力的选项("marketId", "runnerId")-- 这允许以非常直接的方式直接获得答案。计划者为该备选方案分配了非常低的成本,从而使其被选中。如果您不想添加索引,则可以通过执行以下操作来强制设置优化障碍:

SELECT min("runnerId")
  FROM (SELECT "runnerId" FROM betlog
          WHERE "marketId" = '107416794'
          OFFSET 0) x;

当有一个OFFSET子句(即使偏移量为零)时,它会强制单独计划子查询,并将其结果提供给外部查询。我希望这将在 80 毫秒内运行,而不是在没有优化障碍的情况下运行的 1600 毫秒。当然,如果可以加索引的话,缓存数据时的查询速度应该在1ms以内。

于 2012-11-25T18:32:40.290 回答
1

PostgreSQL 将使用对整个表的顺序扫描来执行 min 语句。您可以使用以下方法优化查询: SELECT col FROM sometable ORDER BY col ASC LIMIT 1;

于 2012-11-24T22:41:02.657 回答