PostgreSQL: как проиндексировать все внешние ключи? - PullRequest
14 голосов
/ 04 июня 2010

Я работаю с большой базой данных PostgreSQL и пытаюсь настроить ее, чтобы повысить производительность.

Похоже, что наши запросы и обновления делают много поисков с использованием внешних ключей.

Что мне хотелось бы, так это относительно простой способ добавления индексов ко всем нашим внешним ключам без необходимости проходить каждую таблицу (~ 140) и делать это вручную.

Исследуя это, я пришел к выводу, что Postgres не может сделать это для вас автоматически (как MySQL), но я был бы рад услышать и другое.

Ответы [ 3 ]

18 голосов
/ 04 июня 2010

РЕДАКТИРОВАТЬ : поэтому я написал запрос ниже, а затем подумал ... "Подожди, Postgresql требует, чтобы цели внешнего ключа имели уникальные индексы". Так что, наверное, я неправильно понял, что вы имели в виду? Вы можете использовать приведенный ниже запрос, чтобы проверить, имеют ли исходные ваших внешних ключей индексы, заменив «conrelid» на «confrelid» и «conkey» на «confkey» (да, да, в запросе нет псевдонимов ...)

Ну, я думаю, что должно быть возможно просмотреть системные каталоги ... Как обычно, лучшим руководством по системным каталогам является использование psql и выполнение "\ set ECHO_HIDDEN 1", а затем посмотреть, какой SQL он генерирует для интересные команды "\ d". Вот SQL, используемый для поиска внешних ключей для таблицы ("\ d имя_таблицы"):

-- $1 is the table OID, e.g. 'tablename'::regclass
SELECT conname, conrelid::pg_catalog.regclass,
  pg_catalog.pg_get_constraintdef(c.oid, true) as condef
FROM pg_catalog.pg_constraint c
WHERE c.confrelid = $1 AND c.contype = 'f' ORDER BY 1;

Кажется, что pg_constraint имеет столбцы conkey и confkey, которые выглядят так, как будто они могут быть номерами столбцов, по которым определен ключ. Вероятно, confkey - это номера столбцов во внешней таблице, поскольку для внешних ключей он не равен нулю. Кроме того, мне потребовалось некоторое время, чтобы понять, что это SQL для отображения внешних ключей , ссылающихся на данной таблицы. Что в любом случае мы хотим.

Итак, этот запрос показывает, что данные начинают обретать форму:

select confrelid, conname, column_index, attname
from pg_attribute
     join (select confrelid::regclass, conname, unnest(confkey) as column_index
           from pg_constraint
           where confrelid = 'ticket_status'::regclass) fkey
          on fkey.confrelid = pg_attribute.attrelid
             and fkey.column_index = pg_attribute.attnum

Я собираюсь использовать 8.4 функций, таких как unnest ... вы можете обойтись без.

Я закончил с:

select pg_index.indexrelid::regclass, 'create index ' || relname || '_' ||
         array_to_string(column_name_list, '_') || '_idx on ' || confrelid ||
         ' (' || array_to_string(column_name_list, ',') || ')'
from (select distinct
       confrelid,
       array_agg(attname) column_name_list,
       array_agg(attnum) as column_list
     from pg_attribute
          join (select confrelid::regclass,
                 conname,
                 unnest(confkey) as column_index
                from (select distinct
                        confrelid, conname, confkey
                      from pg_constraint
                        join pg_class on pg_class.oid = pg_constraint.confrelid
                        join pg_namespace on pg_namespace.oid = pg_class.relnamespace
                      where nspname !~ '^pg_' and nspname <> 'information_schema'
                      ) fkey
               ) fkey
               on fkey.confrelid = pg_attribute.attrelid
                  and fkey.column_index = pg_attribute.attnum
     group by confrelid, conname
     ) candidate_index
join pg_class on pg_class.oid = candidate_index.confrelid
left join pg_index on pg_index.indrelid = confrelid
                      and indkey::text = array_to_string(column_list, ' ')

ОК, это чудовище печатает команды-кандидаты в индексы и пытается сопоставить их с существующими индексами. Таким образом, вы можете просто добавить «где indexrelid равен нулю» в конце, чтобы получить команды для создания индексов, которые, кажется, не существуют.

Этот запрос не очень хорошо работает с внешними ключами из нескольких столбцов; но imho, если вы используете их, вы заслуживаете неприятностей.

ПОСЛЕДНЕЕ РЕДАКТИРОВАНИЕ : вот запрос с предложенными правками вверху. Таким образом, показаны команды для создания несуществующих индексов для столбцов, являющихся источником внешнего ключа. (не его цель).

select pg_index.indexrelid::regclass, 'create index ' || relname || '_' ||
         array_to_string(column_name_list, '_') || '_idx on ' || conrelid ||
         ' (' || array_to_string(column_name_list, ',') || ')'
from (select distinct
       conrelid,
       array_agg(attname) column_name_list,
       array_agg(attnum) as column_list
     from pg_attribute
          join (select conrelid::regclass,
                 conname,
                 unnest(conkey) as column_index
                from (select distinct
                        conrelid, conname, conkey
                      from pg_constraint
                        join pg_class on pg_class.oid = pg_constraint.conrelid
                        join pg_namespace on pg_namespace.oid = pg_class.relnamespace
                      where nspname !~ '^pg_' and nspname <> 'information_schema'
                      ) fkey
               ) fkey
               on fkey.conrelid = pg_attribute.attrelid
                  and fkey.column_index = pg_attribute.attnum
     group by conrelid, conname
     ) candidate_index
join pg_class on pg_class.oid = candidate_index.conrelid
left join pg_index on pg_index.indrelid = conrelid
                      and indkey::text = array_to_string(column_list, ' ')
where indexrelid is null

По моему опыту, это не так уж и полезно. Он предлагает создать индексы для таких вещей, как ссылочные коды, которые действительно не нужно индексировать.

6 голосов
/ 04 июня 2010

Информация находится внутри каталожных таблиц . Но, кажется, не очень просто сделать то, что вам нужно, особенно если уже созданы некоторые индексы (и как насчет многоколоночных индексов ...)

Если у вас нет индексированного FK, вы можете сделать что-то быстрое и грязное, например

 SELECT 'CREATE INDEX ' || table_name || '_' || column_name || '_idx ON '
   || table_name || '(' || column_name || ');'
from foreign_key_tables where schema = 'public';

Вы бы заменили интересующей вас схемой, сбросили ее в файл, отредактировали, проверили, помолились и передали в psql. ВНИМАНИЕ, эта процедура не обнаруживает уже существующие индексы.

Ах, foreign_key_tables - это информационное представление, созданное как:

CREATE VIEW foreign_key_tables AS SELECT
    n.nspname AS schema,
    cl.relname AS table_name,
    a.attname AS column_name,
    ct.conname AS key_name,
    nf.nspname AS foreign_schema,
    clf.relname AS foreign_table_name,
    af.attname AS foreign_column_name,
    pg_get_constraintdef(ct.oid) AS create_sql
FROM pg_catalog.pg_attribute a
JOIN pg_catalog.pg_class cl ON (a.attrelid = cl.oid AND cl.relkind =
'r')
JOIN pg_catalog.pg_namespace n ON (n.oid = cl.relnamespace)
JOIN pg_catalog.pg_constraint ct ON (a.attrelid = ct.conrelid AND
ct.confrelid != 0 AND ct.conkey[1] = a.attnum)
JOIN pg_catalog.pg_class clf ON (ct.confrelid = clf.oid AND clf.relkind
= 'r')
JOIN pg_catalog.pg_namespace nf ON (nf.oid = clf.relnamespace)
JOIN pg_catalog.pg_attribute af ON (af.attrelid = ct.confrelid AND
af.attnum = ct.confkey[1]);
0 голосов
/ 12 января 2015

Я создал скрипт с этим кодом, кажется, немного короче:

SELECT 'DROP INDEX IF EXISTS fk_' || conname || '_idx; CREATE INDEX fk_' || conname || '_idx ON ' 
       || relname || ' ' || 
       regexp_replace(
           regexp_replace(pg_get_constraintdef(pg_constraint.oid, true), 
           ' REFERENCES.*$','',''), 'FOREIGN KEY ','','') || ';'
FROM pg_constraint 
JOIN pg_class 
    ON (conrelid = pg_class.oid)
JOIN pg_namespace
    ON (relnamespace = pg_namespace.oid)
WHERE contype = 'f'
  AND nspname = 'public'
  --AND 'fk_' || conname || '_idx' NOT IN (SELECT indexname FROM pg_indexes)
  ;

комментарий в последней строке, если вы не хотите воссоздавать уже существующие индексы

...