我需要runnerId
的最低值。
此查询:
SELECT "runnerId" FROM betlog WHERE "marketId" = '107416794' ;
需要80毫秒(1968个结果行)。
此:
SELECT min("runnerId") FROM betlog WHERE "marketId" = '107416794' ;
需要1600毫秒。
有没有更快的方法来找到最小值,还是应该在我的java程序中计算min?
"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 :(得分:8)
您需要的是multi-column index:
CREATE INDEX betlog_mult_idx ON betlog ("marketId", "runnerId");
如果有兴趣,您可以在this related question on dba.SE下找到有关PostgreSQL中的多列索引,链接和基准的详细信息。
我是怎么想的?
在多列索引中,行由索引的第一列(“marketId”)排序(从而聚集),并且每个簇依次由索引的第二列排序 - 因此第一行与条件匹配min("runnerId")
。这使得索引扫描非常快。
关于LIMIT
减慢查询的悖论效应 - Postgres查询规划器在那里有一个弱点。常见的解决方法是使用CTE(在这种情况下不)。根据最近这个密切相关的问题查找更多信息:
PostgreSQL query taking too long
答案 1 :(得分:1)
min语句将由PostgreSQL使用整个表的顺序扫描执行。您可以使用以下方法优化查询: SELECT col FROM sometable ORDER BY col ASC LIMIT 1;
答案 2 :(得分: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毫秒运行。当然,如果可以添加索引,则缓存数据时查询的速度应小于1毫秒。