Postgres是否自动在外键和主键上放置索引?我怎么知道?是否有一条命令将返回表上的所有索引?
Postgres是否自动在外键和主键上放置索引?我怎么知道?是否有一条命令将返回表上的所有索引?
Answers:
PostgreSQL自动在主键和唯一约束上创建索引,但不在外键关系的引用端创建索引。
当Pg创建隐式索引时,它将发出NOTICE
-level消息,您可以在psql
和/或系统日志中看到该消息,因此可以看到它何时发生。自动创建的索引在\d
表的输出中也可见。
PostgreSQL自动为每个唯一约束和主键约束创建一个索引,以强制执行唯一性。因此,没有必要为主键列显式创建索引。
有关约束的文档说:
由于从被引用表中删除一行或对被引用列进行UPDATE都需要对引用表进行扫描以查找与旧值匹配的行,因此对索引列进行索引通常是一个好主意。因为这并不总是需要的,并且在如何建立索引方面有很多选择,所以外键约束的声明不会自动在引用列上创建索引。
因此,如果需要,您必须自己在外键上创建索引。
请注意,如果您使用主外键,例如2对FK作为M-N表中的PK,则您将在PK上有一个索引,并且可能不需要创建任何额外的索引。
虽然在引用侧外键列上(或包括)创建索引通常是一个好主意,但这不是必需的。每个索引添加减慢DML操作略有下降,所以你在每交纳履约成本INSERT
,UPDATE
或DELETE
。如果很少使用该索引,则可能不值得使用。
如果要从程序中列出模式中所有表的索引,则所有信息都在目录中:
select
n.nspname as "Schema"
,t.relname as "Table"
,c.relname as "Index"
from
pg_catalog.pg_class c
join pg_catalog.pg_namespace n on n.oid = c.relnamespace
join pg_catalog.pg_index i on i.indexrelid = c.oid
join pg_catalog.pg_class t on i.indrelid = t.oid
where
c.relkind = 'i'
and n.nspname not in ('pg_catalog', 'pg_toast')
and pg_catalog.pg_table_is_visible(c.oid)
order by
n.nspname
,t.relname
,c.relname
如果要进一步研究(例如列和顺序),则需要查看pg_catalog.pg_index。使用psql -E [dbname]
可以很方便地弄清楚如何查询目录。
\di
还将列出数据库中的所有索引。(注释从其他答案中复制,也适用于此)
该查询将列出外键(原始来源)上缺少的索引。
编辑:请注意,它不会检查小表(少于9 MB)和其他一些情况。见最终WHERE
声明。
-- check for FKs where there is no matching index
-- on the referencing side
-- or a bad index
WITH fk_actions ( code, action ) AS (
VALUES ( 'a', 'error' ),
( 'r', 'restrict' ),
( 'c', 'cascade' ),
( 'n', 'set null' ),
( 'd', 'set default' )
),
fk_list AS (
SELECT pg_constraint.oid as fkoid, conrelid, confrelid as parentid,
conname, relname, nspname,
fk_actions_update.action as update_action,
fk_actions_delete.action as delete_action,
conkey as key_cols
FROM pg_constraint
JOIN pg_class ON conrelid = pg_class.oid
JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
JOIN fk_actions AS fk_actions_update ON confupdtype = fk_actions_update.code
JOIN fk_actions AS fk_actions_delete ON confdeltype = fk_actions_delete.code
WHERE contype = 'f'
),
fk_attributes AS (
SELECT fkoid, conrelid, attname, attnum
FROM fk_list
JOIN pg_attribute
ON conrelid = attrelid
AND attnum = ANY( key_cols )
ORDER BY fkoid, attnum
),
fk_cols_list AS (
SELECT fkoid, array_agg(attname) as cols_list
FROM fk_attributes
GROUP BY fkoid
),
index_list AS (
SELECT indexrelid as indexid,
pg_class.relname as indexname,
indrelid,
indkey,
indpred is not null as has_predicate,
pg_get_indexdef(indexrelid) as indexdef
FROM pg_index
JOIN pg_class ON indexrelid = pg_class.oid
WHERE indisvalid
),
fk_index_match AS (
SELECT fk_list.*,
indexid,
indexname,
indkey::int[] as indexatts,
has_predicate,
indexdef,
array_length(key_cols, 1) as fk_colcount,
array_length(indkey,1) as index_colcount,
round(pg_relation_size(conrelid)/(1024^2)::numeric) as table_mb,
cols_list
FROM fk_list
JOIN fk_cols_list USING (fkoid)
LEFT OUTER JOIN index_list
ON conrelid = indrelid
AND (indkey::int2[])[0:(array_length(key_cols,1) -1)] @> key_cols
),
fk_perfect_match AS (
SELECT fkoid
FROM fk_index_match
WHERE (index_colcount - 1) <= fk_colcount
AND NOT has_predicate
AND indexdef LIKE '%USING btree%'
),
fk_index_check AS (
SELECT 'no index' as issue, *, 1 as issue_sort
FROM fk_index_match
WHERE indexid IS NULL
UNION ALL
SELECT 'questionable index' as issue, *, 2
FROM fk_index_match
WHERE indexid IS NOT NULL
AND fkoid NOT IN (
SELECT fkoid
FROM fk_perfect_match)
),
parent_table_stats AS (
SELECT fkoid, tabstats.relname as parent_name,
(n_tup_ins + n_tup_upd + n_tup_del + n_tup_hot_upd) as parent_writes,
round(pg_relation_size(parentid)/(1024^2)::numeric) as parent_mb
FROM pg_stat_user_tables AS tabstats
JOIN fk_list
ON relid = parentid
),
fk_table_stats AS (
SELECT fkoid,
(n_tup_ins + n_tup_upd + n_tup_del + n_tup_hot_upd) as writes,
seq_scan as table_scans
FROM pg_stat_user_tables AS tabstats
JOIN fk_list
ON relid = conrelid
)
SELECT nspname as schema_name,
relname as table_name,
conname as fk_name,
issue,
table_mb,
writes,
table_scans,
parent_name,
parent_mb,
parent_writes,
cols_list,
indexdef
FROM fk_index_check
JOIN parent_table_stats USING (fkoid)
JOIN fk_table_stats USING (fkoid)
WHERE table_mb > 9
AND ( writes > 1000
OR parent_writes > 1000
OR parent_mb > 10 )
ORDER BY issue_sort, table_mb DESC, table_name, fk_name;
where
条款:除其他事项外,它仅考虑大小超过9 MB的表。
我喜欢EclipseLink 2.5的出色性能特性一文中的解释
索引外键
第一个功能是自动索引外键。大多数人错误地认为数据库默认情况下会索引外键。好吧,他们没有。主键是自动索引的,但外键不是。这意味着任何基于外键的查询都将进行全表扫描。这是任何OneToMany, ManyToMany或ElementCollection关系,以及许多 OneToOne 关系,以及对涉及联接或对象比较的任何关系的大多数查询。这可能是一个主要的执行问题,您应该始终索引外键字段。