postgres索引查询运行速度低于预期

时间:2015-05-23 19:27:53

标签: sql postgresql indexing

我有一个包含约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个。我在这里缺少什么?

2 个答案:

答案 0 :(得分:2)

看起来您正在推动选择x的限制,其中y IN(...)类型查询。你基本上有一个非常大的表,有很多条件可以搜索。

根据索引的类型,我猜测你有B + Tree这种查询是低效的。这些类型的索引适用于通用范围匹配和数据库插入,而单值查找则表现更差。对于单个值,您的查询正在对此索引执行~2500次查找。

你有几个选择来解决这个问题......

  • 使用哈希索引(这些索引在单值查找上表现得更好)
  • 通过添加一些基于范围的约束来帮助查询优化器,这样您就可以获取2500个值并找到最小值和最大值并将其添加到查询中。所以基本上追加x_id> min_val和x_id< MAX_VAL
  • 如果您有多个数据库后端,则以并行模式运行查询,只需将2500个约束分解为100个组,然后立即运行所有查询并收集结果。如果根据值
  • 对约束进行分组会更好

第一个选项肯定更容易,但是以降低插入/删除速度为代价。

第二种不会受此影响,您甚至不需要将其限制为最小最小值组。您可以使用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具有此类功能)。在这种情况下,服务器应该能够从索引本身检索所需的所有数据,而无需在主表中进行额外的查找。使其成为所谓的“覆盖”指数。