我有一个包含约250列和10米行的表。我在带有IN查询的索引列上选择带有where子句的3列。 IN子句中的id数量为2500,输出限制为1000行,这是粗略的查询:
valgrind --log-fd=9 9>>test.log ./app
这个查询比我预期的要长得多,~1s。在索引的整数列上只有2500个要匹配的项目,看起来这应该更快?也许我的假设不正确。这是解释:
http://explain.depesz.com/s/HpL9
为了简单起见,我没有将所有2500个ID粘贴到EXPLAIN中,因此请忽略这一事实,即只有3个。我在这里缺少什么?
答案 0 :(得分:2)
看起来您正在推动选择x的限制,其中y IN(...)类型查询。你基本上有一个非常大的表,有很多条件可以搜索。
根据索引的类型,我猜测你有B + Tree这种查询是低效的。这些类型的索引适用于通用范围匹配和数据库插入,而单值查找则表现更差。对于单个值,您的查询正在对此索引执行~2500次查找。
你有几个选择来解决这个问题......
第一个选项肯定更容易,但是以降低插入/删除速度为代价。
第二种不会受此影响,您甚至不需要将其限制为最小最小值组。您可以使用N min和max约束创建N个组。使用不同的分组进行测试,看看哪些有效。
最后一个选项当然是最佳表现。
答案 1 :(得分:0)
您的查询相当于:
select col1, col2, col3
from table1
where
col4 = 1
OR col4 = 2
OR col4 = 3
OR col4 = 4
... repeat 2500 times ...
相当于:
select col1, col2, col3
from table1
where col4 = 1
UNION
select col1, col2, col3
from table1
where col4 = 2
UNION
select col1, col2, col3
from table1
where col4 = 3
... repeat 2500 times ...
基本上,这意味着对具有10M行的表的索引进行了2500次搜索。最重要的是,如果col4
不唯一,则每次搜索都是扫描,可能会返回许多行。然后将2500个中间结果集合在一起。
服务器不知道IDs
子句中列出的2500 IN
不重复。它不知道它们已经排序了。因此,它几乎没有选择,但做了2500次独立的索引搜索,记住某个地方的中间结果(比如隐式临时表),然后将它们组合在一起。
如果您有一个单独的表table_with_ids
,其列表为2500 IDs
,其ID
上有主键或唯一键,则服务器会知道它们是唯一的排序。
您的查询将是这样的:
select col1, col2, col3
from
table_with_ids
inner join table1 on table_with_ids.id = table1.col4
服务器可能能够更有效地执行此类join
。
我会使用2500 IDs
的预先填充(临时)表来测试性能,并将其与原始表进行比较。如果差异很大,您可以进一步调查。
实际上,我首先要运行这个简单的查询:
select col1, col2, col3
from table1
where
col4 = 1
并测量运行所需的时间。你不能比这更好。因此,您将有一个下限,并清楚地表明您可以和不可以实现的目标。然后,可以将其更改为where col4 in (1,2)
,看看情况如何变化。
提高性能的另一种方法是让索引不仅在col4
上,而且在col4, col1, col2, col3
上。它仍然是一个索引,但在几个列上。 (在SQL Server中,我会在col1, col2, col3
的索引中包含col4
“列”,而不是索引本身的一部分,以使其更小,但我不认为Postgres具有此类功能)。在这种情况下,服务器应该能够从索引本身检索所需的所有数据,而无需在主表中进行额外的查找。使其成为所谓的“覆盖”指数。