多列索引和性能


31

我有一个带有多列索引的表,并且我对正确排序索引以在查询中获得最大性能存有疑问。

场景:

  • PostgreSQL 8.4,具有约一百万行的表

  • c1列中的值可以有大约100个不同的值。我们可以假设这些值是均匀分布的,因此每个可能的值都有大约10000行。

  • c2可以具有1000个不同的值。每个可能的值都有1000行。

搜索数据时,条件始终包含这两列的值,因此该表具有包含c1和c2的多列索引。如果您仅使用一列进行过滤的查询,我已经读到了正确排序多列索引中的列的重要性。在我们的方案中情况并非如此。

我的问题是这个:

考虑到一个过滤器选择的数据要少得多的事实,如果第一个索引的选择性最强(允许使用较小的数据集),我是否可以提高性能?在看到引用的文章中的图形之前,我从未考虑过这个问题:

在此处输入图片说明

引用的文章中有关多列索引的图像

查询使用两列中的值进行过滤。我没有只使用一列进行过滤的查询。它们都是:WHERE c1=@ParameterA AND c2=@ParameterB。也有类似这样的条件:WHERE c1 = "abc" AND c2 LIKE "ab%"

Answers:


36

回答

由于您访问该网站use-the-index-luke.com,请考虑以下章节:

使用索引,卢克›哪里子句›搜索范围› 更大,更少和之间

它有一个示例可以完美地满足您的情况(两列索引,一个经过相等性测试,另一个用于range),解释了(使用更多这些漂亮的索引图形)为什么@ypercube的建议是准确的并进行总结:

Rule of thumb: index for equality first  then for ranges.

也只适合一列吗?

对于仅一列的查询该怎么办似乎很清楚。在以下相关问题下,有关此方面的更多详细信息和基准:

选择性较低的列优先?

除此之外,如果两个列只有相等条件怎么办?

没关系。将列放在最有可能接收其自身条件的列上,这实际上很重要。

考虑这个演示,或自己复制它。我创建了一个简单的表,其中包含两列,每行包含10万行。一个具有很少的值,另一个具有许多不同的值:

CREATE TEMP TABLE t AS
SELECT (random() * 10000)::int AS lots
     , (random() * 4)::int     AS few
FROM generate_series (1, 100000);

DELETE FROM t WHERE random() > 0.9;  -- create some dead tuples, more "real-life"

ANALYZE t;

SELECT count(distinct lots)   -- 9999
     , count(distinct few)    --    5
FROM   t;

查询:

SELECT *
FROM   t
WHERE  lots = 2345
AND    few = 2;

EXPLAIN ANALYZE 输出(十个最佳,以排除缓存影响):

在t上进行序列扫描(成本= 0.00..5840.84行= 2宽度= 8)
               (实际时间= 5.446..15.535行= 2个循环= 1)
  过滤器:((手数= 2345)AND(很少= 2))
  缓冲区:本地点击数= 443
总运行时间:15.557毫秒

添加索引,重新测试:

CREATE INDEX t_lf_idx ON t(lots, few);
在t上使用t_lf_idx进行索引扫描(成本= 0.00..3.76行= 2宽度= 8)
                                (实际时间= 0.008..0.011行= 2循环= 1)
  指数条件:((手数= 2345)AND(很少= 2))
  缓冲区:本地命中= 4
总运行时间:0.027毫秒

添加其他索引,重新测试:

DROP INDEX t_lf_idx;
CREATE INDEX t_fl_idx  ON t(few, lots);
在t上使用t_fl_idx进行索引扫描(成本= 0.00..3.74行= 2宽度= 8)
                                (实际时间= 0.007..0.011行= 2循环= 1)
  索引条件:([few = 2)AND(lots = 2345))
  缓冲区:本地命中= 4
总运行时间:0.027毫秒

索引中的3列(或更多列)是否也是这种情况?
Hayd

@ hayd:不确定“ this”是指什么。您可能会问一个新问题。您可以随时参考此内容。(并在此处添加评论以链接回。)
Erwin Brandstetter

“这个”的意思是“如果索引定义中有两列以上,索引定义的排序是否重要”
hayd

@hayd:最重要的一点:btree索引适合在前导索引表达式上具有相等条件的查询。其中的顺序几乎无关紧要。许多其他细节将不适合发表评论……
Erwin Brandstetter

谢谢,我将尝试写一个连贯的问题并链接到它。
Hayd

11

如您所说,如果涉及这两列的查询都是对两列的相等性检查,例如:

WHERE c1=@ParameterA AND c2=@ParameterB

不要为此烦恼。我怀疑会不会有任何差异,如果有差异,那可以忽略不计。当然,您始终可以使用数据和服务器设置进行测试。在优化方面,不同版本的DBMS的行为可能略有不同。

索引内的顺序对于其他类型的查询很重要,仅检查一列,或者不等式条件,或者一列的条件,另一列的分组,等等。

如果要选择两个顺序之一,则可以选择将选择性较低的列放在第一位。考虑带有列year和的表month。您更可能需要一个WHERE year = 2000条件或a WHERE year BETWEEN 2000 AND 2013或a WHERE (year, month) BETWEEN (1999, 6) AND (2000, 5)

WHERE month = 7 GROUP BY year可能需要确定类型的查询(查找7月出生的人),但查询频率会降低。当然,这取决于表中存储的实际数据。现在说一个订单,然后说(c1, c2),您以后随时可以添加另一个索引(c2, c1)


在OP评论后更新

也有类似这样的条件: WHERE c1 = 'abc' AND c2 LIKE 'ab%'

此类查询是否恰好是c2列上的范围条件,并且需要(c1, c2)索引。如果您还有反向查询:

WHERE c2 = 'abc' AND c1 LIKE 'ab%'

那么如果您还有(c2, c1)索引,那将是很好的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.