我有几个关于 PostgreSQL 中索引工作的问题。我有一个Friends
带有以下索引的表:
Friends ( user_id1 ,user_id2)
user_id1
并且是表user_id2
的外键user
这些是等价的吗?如果不是那为什么?
Index(user_id1,user_id2) and Index(user_id2,user_id1)
如果我创建主键(user_id1,user_id2),它会自动为它创建索引吗?
如果第一个问题中的索引不相等,那么在上面的主键命令上创建了哪个索引?
这个答案是关于(默认)B-tree索引。稍后见,有关 GiST、GIN 等的相关答案:
以下是在多列索引的第二列上查询表的结果。
任何人都可以轻松复制这些效果。在家里试试。
我在 Debian 上使用 23322 行的中型数据库表在 Debian 上测试了 PostgreSQL 9.0.5 。
adr
它实现了表(地址)和(属性)之间的 n:m 关系att
,但这与这里无关。简化模式:该
UNIQUE
约束有效地实现了唯一索引。我用一个简单的索引重复了测试,以确保得到与预期相同的结果。该表聚集在
adratt_uni
索引上,在我运行测试之前:对查询的顺序扫描
(adr_id, att_id)
尽可能快。多列索引仍然可以单独用于第二个索引列的查询条件。我运行了几次查询以填充缓存,并选择了十次运行中最好的一次以获得可比较的结果。
1.使用两列查询
输出
EXPLAIN ANALYZE
:2.使用第一列查询
输出
EXPLAIN ANALYZE
:3.使用第二列查询
输出
EXPLAIN ANALYZE
:4.禁用indexscan & bitmapscan
EXPLAIN ANALYZE 的输出:
输出
EXPLAIN ANALYZE
:结论
正如预期的那样,多列索引仅用于第二列的查询。
正如预期的那样,它的效率较低,但查询仍然比没有索引快 3 倍。
禁用索引扫描后,查询计划器选择位图堆扫描,其执行速度几乎一样快。只有在禁用它之后,它才会回退到顺序扫描。
请参阅手册中原始报价的其他答案。
自 Postgres 9.0 以来的更新
在 Postgres 13 中,一切仍然基本正确。最显着的变化:
INCLUDE
都支持指数表现。(不过,顺序扫描也变得更快了。)
重新 1) 是和否。
对于同时使用两列的查询,例如
where (user_id1, user_id2) = (1,2)
创建哪个索引并不重要。对于仅在其中一个列上具有条件的查询,例如
where user_id1 = 1
它确实很重要,因为通常只有“前导”列可以用于优化器的比较。因此where user_id1 = 1
可以使用索引 (user_id1, user_id2) 但不能在所有情况下都使用索引 (user_id2, user_id1)。在玩过这个之后(在 Erwin 好心地向我们展示了它的工作设置之后),似乎这在很大程度上取决于第二列的数据分布,尽管我还没有发现哪种情况使优化器能够使用尾随列对于 WHERE 条件。
Oracle 11 也可以(有时)使用不在索引定义开头的列。
re 2) 是的,它会创建一个索引
从手册中引用
re 2a)
Primary Key (user_id1,user_id2)
将在 (user_id1,user_id2) 上创建一个索引(您可以通过简单地创建这样的主键很容易地自己找到它)我强烈建议您阅读手册中关于索引的章节,它基本上回答了上述所有问题。
另外,要创建什么索引?depesz 很好地解释了索引列和其他与索引相关的主题的顺序。
广告 1)
PostgreSQL 中有一些限制,如 @a_horse_with_no_name 描述的。在8.0 版之前,多列索引只能用于对前导列的查询。这在 8.1 版中得到了改进。Postgres 14 (更新)的当前手册解释说:
强调我的。我可以从经验中证实这一点。
另请参阅测试用例在此处添加了我稍后的答案。
这是对杰克的回答的回复,评论不会做。
在9.2 版本之前的 PostgreSQL中没有覆盖索引。由于 MVCC 模型,必须访问结果集中的每个元组以检查可见性。您可能正在考虑 Oracle。
PostgreSQL 开发人员谈论“仅索引扫描”。事实上,该功能已随 Postgres 9.2 一起发布。阅读提交信息。
Depesz 写了一篇内容丰富的博客文章。
INCLUDE
Postgres 11的子句引入了真正的覆盖索引(更新)。相关:这也有点不对劲:
正如我在其他答案的评论中所报告的那样,我还使用一个包含两个整数的表运行测试,仅此而已。索引包含与表相同的列。btree 索引的大小约为表的 2/3。不足以解释因子 3 的加速。根据您的设置,我运行了更多测试,简化为两列和 100000 行。在我的 PostgreSQL 9.0 安装中,结果是一致的。
如果表有额外的列,索引的加速会变得更加显着,但这肯定不是唯一的因素。
概括
多列索引可用于对非前导列进行查询的选择性标准,但根据表和索引元组的大小和可见性,加速只是一个很小的因素。较高的行表示较宽的行,较低的表示结果集中表的较大部分。
如果性能很重要,请首先使用这些列创建一个附加索引。
如果所有涉及的列都包含在索引中(覆盖索引)并且所有涉及的行(每个块)对所有事务都是可见的,那么您可以在 Postgres 9.2 或更高版本中获得“仅索引扫描” 。
这些不是等效的,一般来说 index(bar,baz) 对于表单的查询效率不高
select * from foo where baz=?
Erwin已经证明这样的索引确实可以加快查询速度,但是这种效果是有限的,并且与您通常期望索引改进查找的顺序不同 - 它依赖于索引的“全扫描”通常是这样的事实由于表中没有出现在索引中的额外列,因此比索引表的“完整扫描”更快。
总结:索引甚至可以帮助查询非前导列,但是以两种次要和相对次要的方式之一,而不是以您通常期望索引提供帮助的戏剧性方式,因为它是 btree 结构
注意索引可以提供帮助的两种方式是,如果索引的完整扫描比表的完整扫描便宜得多,或者:1. 表查找很便宜(因为它们很少或它们是聚集的),
或者2. 索引被覆盖,所以根本没有表查找,请参阅此处的Erwins 评论试验台:
查询 1(无索引,达到74 个缓冲区):
查询 2(带索引 - 优化器忽略索引 - 再次达到74 个缓冲区):
查询 2(带有索引 - 我们欺骗优化器使用它):
因此,在这种情况下,通过索引访问的速度是30 个缓冲区的两倍——就索引而言,这“稍微快一点”!和 YMMV 取决于表和索引的相对大小,以及过滤行数和集群特征表中的数据
相比之下,对前导列的查询利用索引的 btree 结构——在这种情况下,命中2 个缓冲区: