mad*_*low 5 sql postgresql indexing query-performance
像许多 Postgres 一样,n00bs我们有很多带有外键约束但未建立索引的表。在某些情况下,这不会对性能造成很大影响 - 但这需要进一步分析。
我已阅读以下文章:https://www.cybertec-postgresql.com/en/index-your-foreign-key/
并使用以下查询查找所有没有索引的外键:
SELECT c.conrelid::regclass AS "table",
/* list of key column names in order */
string_agg(a.attname, ',' ORDER BY x.n) AS columns,
pg_catalog.pg_size_pretty(
pg_catalog.pg_relation_size(c.conrelid)
) AS size,
c.conname AS constraint,
c.confrelid::regclass AS referenced_table
FROM pg_catalog.pg_constraint c
/* enumerated key column numbers per foreign key */
CROSS JOIN LATERAL
unnest(c.conkey) WITH ORDINALITY AS x(attnum, n)
/* name for each key column */
JOIN pg_catalog.pg_attribute a
ON a.attnum = x.attnum
AND a.attrelid = c.conrelid
WHERE NOT EXISTS
/* is there a matching index for the constraint? */
(SELECT 1 FROM pg_catalog.pg_index i
WHERE i.indrelid = c.conrelid
/* the first index columns must be the same as the
key columns, but order doesn't matter */
AND (i.indkey::smallint[])[0:cardinality(c.conkey)-1]
@> c.conkey::int[])
AND c.contype = 'f'
GROUP BY c.conrelid, c.conname, c.confrelid
ORDER BY pg_catalog.pg_relation_size(c.conrelid) DESC;
Run Code Online (Sandbox Code Playgroud)
这向我展示了具有复合唯一约束的表,只有唯一索引中的“一”列:
\d topics_items;
-----------------+---------+--------------+---------------+------------------------------
topics_items_id | integer | | not null | generated always as identity
topic_id | integer | | not null |
item_id | integer | | not null |
Index:
"topics_items_pkey" PRIMARY KEY, btree (topics_items_id)
"topic_id_item_id_unique" UNIQUE CONSTRAINT, btree (topic_id, item_id)
Foreign Keys:
"topics_items_item_id_fkey" FOREIGN KEY (item_id) REFERENCES items(item_id) ON DELETE CASCADE
"topics_items_topic_id_fkey" FOREIGN KEY (topic_id) REFERENCES topics(topic_id) ON DELETE CASCADE
Run Code Online (Sandbox Code Playgroud)
在这种情况下,检查查询仅查找item_id,而不查找作为topic_id未索引字段的 。
公平地说,这只是所使用的查询的问题,我必须单独索引两个字段(topic_id 和 item_id) - 或者是否涉及一些黑色巫术并且只需要item_id索引?
tl;dr您需要在 上添加索引item_id。11. 索引介绍了 Postgres 索引的“黑魔法”。
您有一个复合索引,(topic_id, item_id)并且列顺序很重要。Postgres 可以使用它来对、和topic_id上的查询进行索引,但不能单独使用(或效率较低)。topic_iditem_iditem_id
从11.3 开始。多列索引...
多列 B 树索引可与涉及索引列的任何子集的查询条件一起使用,但当前导(最左边)列存在约束时,索引效率最高。
-- indexed
select *
from topics_items
where topic_id = ?
-- also indexed
select *
from topics_items
where topic_id = ?
and item_id = ?
-- probably not indexed
select *
from topics_items
where item_id = ?
Run Code Online (Sandbox Code Playgroud)
这是因为复合索引(topic_id, item_id)首先存储主题 ID,然后存储也具有该主题 ID 的项目 ID。为了在此索引中有效地查找项目 ID,Postgres 必须首先使用主题 ID 缩小搜索范围。
如果Postgres认为值得付出努力,它可以反转索引。如果可能的主题 ID 较少,而可能的索引 ID 较多,则会在每个主题 ID 中搜索索引 ID。
例如,假设您有 10 个可能的主题 ID 和 1000 个可能的项目 ID 以及您的索引(topic_id, index_id)。这就像有 10 个明确标记的主题 ID 桶,每个桶内有 1000 个明确标记的项目 ID 桶。要获取项目 ID 存储桶,它必须查看每个主题 ID 存储桶内部。要在 Postgres 上使用此索引,where item_id = 23必须在 10 个主题 ID 存储桶中的每一个中搜索项目 ID 为 23 的所有存储桶。
但是,如果您有 1000 个可能的主题 ID 和 10 个可能的项目 ID,Postgres 将必须搜索 1000 个主题 ID 存储桶。它很可能会进行全表扫描。在这种情况下,您需要反转索引并使其成为(item_id, topic_id).
这在很大程度上取决于良好的表统计数据,这意味着确保 autovacuum 正常工作。
因此,如果一列的可变性远小于另一列,那么您可以对两列使用单个索引。
如果 Postgres 认为可以使查询运行得更快,那么它也可以使用多个索引。例如,如果您有一个索引 ontopic_id和一个索引 on item_id,它可以使用这两个索引并合并结果。例如,where topic_id = 23 or item_id = 42可以使用 topic_id 索引搜索主题 ID 23,使用 item_id 索引搜索项目 ID 42,然后合并结果。
这通常比复合(topic_id, item_id)索引慢。它也可能比使用单个索引慢,因此如果 Postgres 决定不使用多个索引,请不要感到惊讶。
一般来说,对于 B 树索引,当您有两列时,您有三种可能的组合。
并且您需要两个索引。
(a, b)涵盖 a 和 a + b 的搜索。(b)涵盖搜索b.
当您有三列时,您有七种可能的组合。
但你只需要三个索引。
但是,您可能实际上希望避免在三列上建立索引。通常会比较慢。你真正想要的是这个。
应谨慎使用多列索引。在大多数情况下,单列上的索引就足够了,并且节省空间和时间。除非表的使用非常程式化,否则具有超过三列的索引不太可能有帮助。
从索引读取比从表读取慢。您希望索引减少必须读取的行数,但不希望 Postgres 执行不必要的索引扫描。
右侧列的约束...在索引中进行检查,因此它们可以节省对表的访问,但不会减少必须扫描的索引部分。例如,给定 (a, b, c) 上的索引和查询条件 WHERE a = 5 AND b >= 42 AND c < 77,必须从 a = 5 且 b = 的第一个条目开始扫描索引42 到 a = 5 的最后一个条目。 c >= 77 的索引条目将被跳过,但仍然必须扫描它们。
| 归档时间: |
|
| 查看次数: |
4855 次 |
| 最近记录: |