2012-11-24 47 views
5

我需要runnerId的最小值。Postgres min函数性能

这个查询:

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如何减缓查询下来?

+0

你有marketid的索引吗? –

+1

[找出声明执行这么长时间的原因是用EXPLAIN命令完成的](http://wiki.postgresql.org/wiki/Using_EXPLAIN) – raina77ow

+0

是的,它有一个索引 – wutzebaer

回答

8

你需要的是一个multi-column index

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

如果有兴趣,你会发现关于PostgreSQL的多列索引深入的信息,this related question on dba.SE下链接和基准。

我是怎么想的?
在多列索引中,行由索引的第一列(“marketId”)排序(并因此聚集),并且每个集群依次由索引的第二列排序 - 因此第一行匹配条件min("runnerId")。这使得索引扫描速度非常快。

关于LIMIT放缓查询的佯谬效应 - Postgres查询规划器在那里存在一个弱点。常见的解决方法是使用CTE(在这种情况下,必须使用而不是)。最近这次,密切相关的问题,在查找更多信息:
PostgreSQL query taking too long

+0

这个问题解决了,你能给我一点背景吗?你是如何认识到这一点的? – wutzebaer

+1

@wutzebaer:我添加了一个指向手册的链接,指向一个问题的链接,其中包含有关多列索引的更多信息和一些解释。 –

+0

这真的很奇怪 - 什么是从“坏”查询解释?处理4065行不应占用1500毫秒。 –

1

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

+0

只是命令ist fast too >> SELECT “runnerId”FROM betlog WHERE“marketId”='107416794'ORDER BY“runnerId”<<但是当我在1600 ms再加上“LIMIT 1”时 – wutzebaer

+0

所以基本上你可以使用没有限制语句的order by方法。这应该优化您的查询。 –

+0

好的,但限制如何减慢查询速度?它的问题,因为我想使用这个查询作为子查询 – wutzebaer

1

当你对("runnerId")索引(或至少"runnerId"为高阶列),但并没有对("marketId", "runnerId")索引它相比的成本使用该列上的索引传递具有匹配的"marketId"的所有行,并从该集合中挑选出最小的"runnerId"到使用"runnerId"上的索引进行扫描的成本,并在找到匹配的第一行"marketId"时停止。根据可用的统计数据和假设"marketId"值将随机分布在"runnerId"上的索引的索引条目内,它估计后一方法的成本较低。

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

问题是假设值将随机分布在范围内并不一定是真实的(如本例中),导致扫描范围的高百分比以找到潜伏在最后的行。对于"marketId"的某些值,在"runnerId"索引开始附近选择的值可用,此计划应该非常快。

PostgreSQL开发人员社区已经讨论过,如果数据分布不是假定的,那么我们如何偏向对长期运行而言是“风险”的计划,并且已经开始跟踪多列统计数据,以便相关值不会遇到这样的问题。在接下来的几个版本中,预计这方面会有所改进。在此之前,欧文的建议是针对如何解决这个问题的目标。

基本上,它归结为制定一个更具吸引力的计划可用或引入优化障碍。在这种情况下,您可以通过在("marketId", "runnerId")上添加索引来提供更具吸引力的选项 - 这可以非常直接地直接找到答案。该计划者为该替代方案分配了一个非常低的成本,导致它被选中。

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

当有OFFSET条款(即使对于零偏移)它迫使子查询单独计划:如果你宁愿不添加索引,你可以做这样的事情迫使优化屏障并将其结果反馈给外部查询。我预计这会在80毫秒内运行,而不是没有优化障碍的1600毫秒。当然,如果您可以添加索引,那么数据缓存时查询的速度应该小于1毫秒。