Postgresは自動的に外部キーと主キーにインデックスを付けますか?どうすればわかりますか?テーブルのすべてのインデックスを返すコマンドはありますか?
Postgresは自動的に外部キーと主キーにインデックスを付けますか?どうすればわかりますか?テーブルのすべてのインデックスを返すコマンドはありますか?
回答:
PostgreSQLは、主キーと一意制約のインデックスを自動的に作成しますが、外部キー関係の参照側には作成しません。
Pgが暗黙のインデックスを作成すると、NOTICE
-levelメッセージが表示されpsql
、システムログで確認できるため、いつ発生したかを確認できます。自動作成されたインデックスは\d
、テーブルの出力にも表示されます。
ユニークインデックス上のドキュメントは言います:
PostgreSQLは一意性を強制するために、一意性制約と主キー制約ごとにインデックスを自動的に作成します。したがって、主キー列に対して明示的に索引を作成する必要はありません。
と制約に関するドキュメントは言う:
参照されるテーブルからの行のDELETEまたは参照される列のUPDATEでは、古い値に一致する行を参照するテーブルをスキャンする必要があるため、参照する列にインデックスを付けることをお勧めします。これは常に必要なわけではなく、インデックスの作成方法には多くの選択肢があるため、外部キー制約を宣言しても、参照する列にインデックスが自動的に作成されるわけではありません。
したがって、必要に応じて、外部キーにインデックスを自分で作成する必要があります。
M-to-NテーブルのPKとして2 FKのような主外部キーを使用する場合、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、 多対多またはElementCollection関係だけでなく、多くの OneToOneの 関係、及び結合やオブジェクトの比較関わるすべての関係上ほとんどのクエリ。これは主要なパフォーマンスの問題になる可能性があり、外部キーフィールドには常にインデックスを付ける必要があります。