jap*_*968 37 postgresql index index-tuning
我有一个带有多列索引的表,我怀疑索引的正确排序以获得最大查询性能。
场景:
PostgreSQL 8.4,大约有一百万行的表
c1列中的值可以有大约100 个不同的值。我们可以假设这些值是均匀分布的,因此每个可能的值大约有 10000 行。
列c2可以有1000 个不同的值。对于每个可能的值,我们有 1000 行。
搜索数据时,条件始终包含这两列的值,因此该表具有组合 c1 和 c2 的多列索引。如果您的查询仅使用一列进行过滤,我已经阅读了正确排序多列索引中的列的重要性。在我们的场景中,情况并非如此。
我的问题是这个:
鉴于其中一个过滤器选择的数据集要小得多,如果第一个索引是最具选择性的索引(允许更小的数据集),我是否可以提高性能?在我看到参考文章中的图形之前,我从未考虑过这个问题:

图片取自有关多列索引的参考文章。
查询使用两列中的值进行过滤。我没有仅使用一列进行过滤的查询。他们都是:WHERE c1=@ParameterA AND c2=@ParameterB。还有这样的条件:WHERE c1 = "abc" AND c2 LIKE "ab%"
Erw*_*ter 40
由于您参考了网站use-the-index-luke.com,请考虑以下章节:
使用索引,Luke › Where 子句 › 搜索范围 ›更大、更少和 BETWEEN
它有一个与您的情况完美匹配的示例(两列索引,一个用于相等性测试,另一个用于range),解释了(用更多那些漂亮的索引图形)为什么@ypercube 的建议是准确的并总结了它:
经验法则:首先是相等的索引 - 然后是范围。
对仅一列的查询做什么似乎很清楚。关于这些相关问题的更多细节和基准:
除此之外,如果两列只有相等条件怎么办?
没关系。将更可能收到其自身条件的列放在首位,这实际上很重要。
一个包含 10 万行的两列简单表格。一种具有很少的,另一种具有许多不同的值。2013 年使用 Postgres 9.2 进行的原始测试:
CREATE TABLE tbl AS
SELECT (random() * 10000)::int AS lots
, (random() * 4)::int AS few
FROM generate_series (1, 100000);
DELETE FROM tbl WHERE random() > 0.9; -- create some dead tuples, more "real-life"
VACUUM ANALYZE tbl;
SELECT count(distinct lots) -- 9999
, count(distinct few) -- 5
FROM tbl;
Run Code Online (Sandbox Code Playgroud)
询问:
SELECT *
FROM tbl
WHERE lots = 2345
AND few = 2;
Run Code Online (Sandbox Code Playgroud)
EXPLAIN ANALYZE 输出(最好的 10 排除缓存效果):
tbl 上的 Seq 扫描(成本 = 0.00..5840.84 行 = 2 宽度 = 8)
(实际时间=5.646..15.535行=2循环=1)
过滤器:((手数 = 2345)AND(少数 = 2))
缓冲区:本地命中=443
总运行时间:15.557 毫秒
添加索引,重新测试:
CREATE INDEX tbl_lf_idx ON tbl(lots, few);Run Code Online (Sandbox Code Playgroud)
在 t 上使用 tbl_lf_idx 进行索引扫描(成本 = 0.00..3.76 行 = 2 宽度 = 8)
(实际时间=0.008..0.011 行=2 循环=1)
指数条件: ((lots = 2345) AND (few = 2))
缓冲区:本地命中=4
总运行时间:0.027 毫秒
添加其他索引,重新测试:
DROP INDEX tbl_lf_idx;
CREATE INDEX tbl_fl_idx ON tbl(few, lots);Run Code Online (Sandbox Code Playgroud)
在 tbl 上使用 tbl_fl_idx 进行索引扫描(成本 = 0.00..3.74 行 = 2 宽度 = 8)
(实际时间=0.007..0.011 行=2 循环=1)
指数条件: ((few = 2) AND (lots = 2345))
缓冲区:本地命中=4
总运行时间:0.027 毫秒
用 Postgres 13 重复 2021,同样的结论:
db<>在这里摆弄
ype*_*eᵀᴹ 11
如果,如您所说,涉及这两列的查询都是两列的相等性检查,例如:
WHERE c1=@ParameterA AND c2=@ParameterB
Run Code Online (Sandbox Code Playgroud)
不要理会这个。我怀疑会有任何差异,如果有差异,则可以忽略不计。当然,您始终可以使用您的数据和服务器设置进行测试。不同版本的 DBMS 在优化方面的行为可能略有不同。
索引内的顺序对于其他类型的查询很重要,只检查一列,或不等式条件,或一列上的条件并在另一列中分组等。
如果我要选择两个订单中的一个,我会选择将选择较少的列放在第一位。考虑一个包含列year和的表month。您更有可能需要WHERE year = 2000条件或 aWHERE 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%'
Run Code Online (Sandbox Code Playgroud)
那么如果你也有一个(c2, c1)索引就好了。