CREATE INDEX
CREATE INDEX — создать индекс
Синтаксис
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ]имя] ON [ ONLY ]имя_таблицы[ USINGметод] ( {имя_столбца| (выражение) } [ COLLATEправило_сортировки] [класс_операторов[ (параметр_класса_оп=значение[, ... ] ) ] ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] ) [ INCLUDE (имя_столбца[, ...] ) ] [ WITH (параметр_хранения[=значение] [, ... ] ) ] [ TABLESPACEтабл_пространство] [ WHEREпредикат]
Описание
CREATE INDEX создаёт индексы по указанному столбцу(ам) заданного отношения, которым может быть таблица или материализованное представление. Индексы применяются в первую очередь для оптимизации производительности базы данных (хотя при неправильном использовании возможен и противоположный эффект).
Ключевое поле для индекса задаётся как имя столбца или выражение, заключённое в скобки. Если метод индекса поддерживает составные индексы, допускается указание нескольких полей.
Поле индекса может быть выражением, вычисляемым из значений одного или нескольких столбцов в строке таблицы. Это может быть полезно для получения быстрого доступа к данным по некоторому преобразованию исходных значений. Например, индекс, построенный по выражению upper(col), позволит использовать поиск по индексу в предложении WHERE upper(col) = 'JIM'.
PostgreSQL предоставляет следующие методы индексов: B-дерево, хеш, GiST, SP-GiST, GIN и BRIN. Пользователи могут определить и собственные методы индексов, но это довольно сложная задача.
Если в команде присутствует предложение WHERE, она создаёт частичный индекс. Такой индекс содержит записи только для части таблицы, обычно более полезной для индексации, чем остальная таблица. Например, если таблица содержит информацию об оплаченных и неоплаченных счетах, при этом последних сравнительно немного, но именно эта часть таблицы наиболее востребована, то увеличить быстродействие можно, создав индекс только по этой части. Ещё одно возможное применение WHERE — добавив UNIQUE, обеспечить уникальность в подмножестве таблицы. Подробнее это рассматривается в Разделе 11.8.
Выражение в предложении WHERE может ссылаться только на столбцы нижележащей таблицы, но не обязательно ограничиваться теми, по которым строится индекс. В настоящее время в WHERE также нельзя использовать подзапросы и агрегатные выражения. Это же ограничение распространяется и на выражения в полях индексов.
Все функции и операторы, используемые в определении индекса, должны быть «постоянными», то есть, их результаты должны зависеть только от аргументов, но не от внешних факторов (например, содержимого другой таблицы или текущего времени). Это ограничение обеспечивает определённость поведения индекса. Чтобы использовать в выражении индекса или в предложении WHERE собственную функцию, не забудьте пометить её при создании как постоянную (IMMUTABLE).
Параметры
UNIQUEУказывает, что система должна контролировать повторяющиеся значения в таблице при создании индекса (если в таблице уже есть данные) и при каждом добавлении данных. Попытки вставить или изменить данные, при которых будет нарушена уникальность индекса, будут завершаться ошибкой.
Когда уникальные индексы применяются к секционированным таблицам, действуют дополнительные ограничения; см. CREATE TABLE.
CONCURRENTLYС этим указанием PostgreSQL построит индекс, не устанавливая никаких блокировок, которые бы предотвращали добавление, изменение или удаление записей в таблице, тогда как по умолчанию операция построения индекса блокирует запись (но не чтение) данных в таблице до своего завершения. С созданием индекса в этом режиме связан ряд особенностей, о которых следует знать, — см. Неблокирующее построение индексов.
Для временных таблиц
CREATE INDEXвсегда выполняется более простым, неблокирующим способом, так как они не могут использоваться никакими другими сеансами.IF NOT EXISTSНе считать ошибкой, если индекс с таким именем уже существует. В этом случае будет выдано замечание. Заметьте, что нет никакой гарантии, что существующий индекс как-то соотносится с тем, который мог бы быть создан. Имя индекса является обязательным, когда указывается
IF NOT EXISTS.INCLUDEНеобязательное предложение
INCLUDEпозволяет указать список столбцов, которые войдут в индекс как неключевые столбцы. Неключевой столбец не может использоваться в условии поиска для сканирования по индексу, и он не учитывается при анализе ограничения уникальности или исключения, устанавливаемого индексом. Однако при сканировании только индекса содержимое неключевых столбцов может быть получено без обращения к целевой таблице, так как оно находится непосредственно в элементе индекса. Таким образом, в результате добавления неключевых столбцов сканирование только индекса может использоваться в тех запросах, где иначе оно было бы неприменимо.Добавляя в индекс неключевые столбцы, особенно большого размера, есть смысл проявлять консерватизм. Если кортеж в индексе превышает максимально допустимый размер для данного типа индексов, вставить данные в таблицу не удастся. В неключевых столбцах дублируются данные из основной таблицы, что приводит к раздуванию индекса и может повлечь замедление запросов. Кроме того, если неключевой столбец содержится в индексе B-дерево, в таком индексе не будет работать исключение дубликатов.
Для столбцов, перечисленных в
INCLUDE, не нужны соответствующие классы операторов; это предложение может содержать столбцы, для типов данных которых не определены классы операторов для заданного метода доступа.Выражения в неключевых столбцах не поддерживаются, так как их нельзя будет использовать при сканировании только индекса.
В настоящее время эту возможность поддерживают методы доступа индексов B-дерево, GiST и SP-GiST. В таких индексах значения столбцов, указанных в предложении
INCLUDE, включаются в кортежи на уровне листьев, которые соответствуют кортежам кучи, но не включаются в элементы верхних уровней, используемые для навигации в дереве.имяИмя создаваемого индекса. Указание схемы при этом не допускается; индекс всегда относится к той же схеме, что и родительская таблица. Если имя опущено, PostgreSQL формирует подходящее имя по имени родительской таблицы и именам индексируемых столбцов.
ONLYУказывает, что индексы не должны рекурсивно создаваться в секциях секционированной таблицы. По умолчанию создание выполняется рекурсивно.
имя_таблицыИмя индексируемой таблицы (возможно, дополненное схемой).
методИмя применяемого метода индекса. Возможные варианты:
btree,hash,gist,spgist,gin,brinили установленный пользователем метод, например bloom. По умолчанию подразумевается методbtree.имя_столбцаИмя столбца таблицы.
выражениеВыражение с одним или несколькими столбцами таблицы. Обычно выражение должно записываться в скобках, как показано в синтаксисе команды. Однако скобки можно опустить, если выражение записано в виде вызова функции.
правило_сортировкиИмя правила сортировки, применяемого для индекса. По умолчанию используется правило сортировки, заданное для индексируемого столбца, либо полученное для результата выражения индекса. Индексы с нестандартными правилами сортировки могут быть полезны для запросов, включающих выражения с такими правилами.
класс_операторовИмя класса операторов. Подробнее об этом ниже.
параметр_класса_опИмя параметра класса операторов. Подробнее об этом ниже.
ASCУказывает порядок сортировки по возрастанию (подразумевается по умолчанию).
DESCУказывает порядок сортировки по убыванию.
NULLS FIRSTУказывает, что значения NULL после сортировки оказываются перед остальными. Это поведение по умолчанию с порядком сортировки
DESC.NULLS LASTУказывает, что значения NULL после сортировки оказываются после остальных. Это поведение по умолчанию с порядком сортировки
ASC.параметр_храненияИмя специфичного для индекса параметра хранения. За подробностями обратитесь к разделу Параметры хранения индекса ниже.
табл_пространствоТабличное пространство, в котором будет создан индекс. Если не определено, выбирается default_tablespace, либо temp_tablespaces, при создании индекса временной таблицы.
предикатВыражение ограничения для частичного индекса.
Параметры хранения индекса
Необязательное предложение WITH определяет параметры хранения для индекса. У каждого метода индекса есть свой набор допустимых параметров хранения. Следующий параметр принимают методы B-дерево, хеш, GiST и SP-GiST:
fillfactor(integer)Фактор заполнения для индекса определяет в процентном отношении, насколько плотно метод индекса будет заполнять страницы индекса. Для B-деревьев концевые страницы заполняются до этого процента при начальном построении индекса и позже, при расширении индекса вправо (добавлении новых наибольших значений ключа). Если страницы впоследствии оказываются заполненными полностью, они будут разделены, что приводит к фрагментации структуры индекса на диске. Для B-деревьев по умолчанию используется фактор заполнения 90, но его можно поменять на любое целое число от 10 до 100.
Для индексов-B-деревьев в таблицах, где ожидается множество добавлений и/или изменений записей, имеет смысл выбрать меньшие значения фактора заполнения в момент выполнения
CREATE INDEX(до массовой загрузки данных в таблицы). Значения в диапазоне 50 – 90 могут сгладить всплески разделений страниц на начальном этапе жизни индекса-B-дерева (при подобном снижении фактора заполнения может даже уменьшиться абсолютное количество разделений страниц, хотя это во многом зависит от нагрузки). Процедура восходящего удаления индексных кортежей, описанная в Подразделе 64.4.2, рассчитывает на наличие некоторого «дополнительного» места на страницах, где будут сохраняться «дополнительные» версии кортежей, и следовательно, может зависеть от фактора заполнения (хотя обычно его влияние второстепенно).В других особых случаях может быть полезно увеличить фактор заполнения до 100 перед выполнением
CREATE INDEX, чтобы максимально эффективно использовать хранилище. Этот вариант следует рассматривать, только если вы абсолютно уверены, что таблица будет статичной (то есть в ней никогда не будут изменяться или добавляться данные). В противном случае фактор заполнения, равный 100, скорее повредит производительности — даже единичные изменения или добавления вызовут резкий скачок активности разделения страниц.Другие индексные методы используют фактор заполнения по-своему, но примерно в том же ключе; значение по умолчанию для разных методов разное.
Индексы B-дерево дополнительно принимают этот параметр:
deduplicate_items(boolean)Этот параметр управляет механизмом исключения дубликатов, описанным в Подразделе 64.4.3. Он принимает логическое значение
ONилиOFF, соответственно включающее или отключающее это оптимизацию. (Другие возможные написанияONиOFFперечислены в Разделе 20.1.) Значение по умолчанию —ON.Примечание
После выключения параметра
deduplicate_itemsкомандойALTER INDEXпри добавлении в будущем новых элементов дубликаты исключаться не будут, но представление существующих кортежей не поменяется на стандартное.
Индексы GiST дополнительно принимают этот параметр:
buffering(enum)Определяет, будет ли при построении индекса использоваться буферизация, описанная в Подразделе 65.4.1. Со значением
OFFона отключена, сON— включена, а сAUTO— отключена вначале, но может затем включиться на ходу, как только размер индекса достигнет значения effective_cache_size. Значение по умолчанию —AUTO. Заметьте, что если возможно построить индекс с сортировкой, именно этот метод будет использоваться при любом значении, кромеbuffering=ON.
Индексы GIN принимают другие параметры:
fastupdate(boolean)Этот параметр управляет механизмом быстрого обновления, описанным в Подразделе 67.4.1. Он имеет логическое значение:
ONвключает быстрое обновление,OFFотключает его. Значение по умолчанию —ON.Примечание
Выключение
fastupdateвALTER INDEXпредотвращает помещение добавляемых в дальнейшем строк в список записей, ожидающих индексации, но записи, добавленные в этот список ранее, в нём остаются. Чтобы очистить очередь операций, надо затем выполнитьVACUUMдля этой таблицы или вызвать функциюgin_clean_pending_list.
gin_pending_list_limit(integer)Пользовательский параметр gin_pending_list_limit. Его значение задаётся в килобайтах.
Индексы BRIN принимают другие параметры:
pages_per_range(integer)Определяет, сколько блоков таблицы образуют зону блоков для каждой записи в индексе BRIN (за подробностями обратитесь к Разделу 68.1). Значение по умолчанию —
128.autosummarize(boolean)Определяет, будет ли поставлен в очередь расчёт сводного значения для предыдущей зоны страниц, когда происходит добавление в следующую. За подробностями обратитесь к Подразделу 68.1.1. По умолчанию
off.
Неблокирующее построение индексов
Создание индекса может мешать обычной работе с базой данных. Обычно PostgreSQL блокирует запись в индексируемую таблицу и выполняет всю операцию построения индекса за одно сканирование таблицы. Другие транзакции могут продолжать читать таблицу, но при попытке вставить, изменить или удалить строки в таблице они будут заблокированы до завершения построения индекса. Это может оказать нежелательное влияние на работу производственной базы данных. Индексация очень больших таблиц может занимать много часов, и даже для маленьких таблиц построение индекса может заблокировать записывающие процессы на время, неприемлемое для производственной системы.
PostgreSQL поддерживает построение индексов без блокировки записи. Этот метод выбирается указанием CONCURRENTLY команды CREATE INDEX. Когда он используется, PostgreSQL должен выполнить два сканирования таблицы, а кроме того, должен дождаться завершения всех существующих транзакций, которые потенциально могут модифицировать и использовать этот индекс. Таким образом, эта процедура требует проделать в сумме больше действий и выполняется значительно дольше, чем обычное построение индекса. Однако благодаря тому, что этот метод позволяет продолжать обычную работу с базой во время построения индекса, он оказывается полезным в производственной среде. Хотя разумеется, дополнительная нагрузка на процессор и подсистему ввода-вывода, создаваемая при построении индекса, может привести к замедлению других операций.
При неблокирующем построении индекса он как «нерабочий» добавляется в системный каталог в одной транзакции, а затем в двух других транзакциях выполняются ещё два сканирования таблицы. Перед каждым сканированием таблицы процедура построения индекса должна ждать завершения текущих транзакций, модифицировавших эту таблицу. После второго сканирования также необходимо дожидаться завершения всех транзакций, получивших снимок (см. Главу 13) перед вторым сканированием, включая транзакции, задействованные на любом этапе неблокирующего построения индексов для других таблиц (это касается частичных индексов и индексов, построенных не просто по столбцам). Наконец индекс может быть помечен как «рабочий» и готовый к использованию, после чего команда CREATE INDEX завершается. Однако даже тогда индекс может быть не готов немедленно к применению в запросах: в худшем случае он не будет использоваться, пока существуют транзакции, начатые до начала построения индекса.
Если при сканировании таблицы возникает проблема, например взаимоблокировка или нарушение уникальности в уникальном индексе, команда CREATE INDEX завершится ошибкой, но оставит после себя «нерабочий» индекс. Этот индекс будет игнорироваться при чтении данных, так как он может быть неполным; однако с ним могут быть связаны дополнительные операции при изменениях. В psql встроенная команда \d помечает такой индекс как INVALID:
postgres=# \d tab
Table "public.tab"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
col | integer | | |
Indexes:
"idx" btree (col) INVALID Рекомендуемый в таких случаях способ исправления ситуации — удалить индекс и затем попытаться снова выполнить CREATE INDEX CONCURRENTLY. (Кроме того, можно перестроить его с помощью команды REINDEX INDEX CONCURRENTLY.)
Ещё одна сложность, с которой можно столкнуться при неблокирующем построении уникального индекса, заключается в том, что ограничение уникальности уже влияет на другие транзакции, когда второе сканирование таблицы только начинается. Это значит, что нарушения ограничения могут проявляться в других запросах до того, как индекс становится доступным для использования и даже тогда, когда создать индекс в итоге не удаётся. Кроме того, если при втором сканировании происходит ошибка, «нерабочий» индекс оставляет в силе своё ограничение уникальности и дальше.
Метод неблокирующего построения поддерживает также индексы выражений и частичные индексы. Ошибки, произошедшие при вычислении этих выражений, могут привести к такому же поведению, как в вышеописанных случаях с нарушением ограничений уникальности.
Обычное построение индекса допускает одновременное построение других индексов для таблицы обычным методом, но неблокирующее построение для конкретной таблицы в один момент времени допускается только одно. Однако в любом случае никакие другие изменения схемы таблицы в это время не разрешаются. Другое отличие состоит в том, что в блоке транзакции может быть выполнена обычная команда CREATE INDEX, но не CREATE INDEX CONCURRENTLY.
Метод неблокирующего построения для индексов секционированных таблиц в настоящее время не поддерживается. Однако вы можете сократить время, на которое будет заблокирована секционированная таблица, построив в неблокирующем режиме индекс для каждой секции в отдельности и затем создав секционированный индекс в обычном режиме. В этом случае построение секционированного индекса будет заключаться только в изменении метаданных.
Примечания
Информацию о том, когда могут применяться, и когда не применяются индексы, и в каких конкретных ситуациях они могут быть полезны, можно найти в Главе 11.
В настоящее время индексы с несколькими ключевыми столбцами поддерживаются только методами B-дерево, GiST, GIN и BRIN. Возможность построения индекса по нескольким ключевым столбцам не зависит от поддержки включения в индекс неключевых столбцов (INCLUDE). Индексы могут содержать до 32 столбцов, в том числе неключевых. (Этот предел можно изменить, пересобрав PostgreSQL.) Уникальные индексы поддерживает только метод B-дерево.
Для каждого столбца индекса можно задать класс операторов и дополнительные параметры класса. Данный класс определяет, какие операторы будут использоваться индексом для этого столбца. Например, индекс-B-дерево по четырёхбайтовым целым будет использовать класс int4_ops; этот класс операторов включает функции сравнения для таких значений. На практике обычно достаточно использовать класс операторов по умолчанию для типа данных столбца. Существование классов операторов объясняется в первую очередь тем, что для некоторых типов данных можно предложить более одного осмысленного порядка сортировки. Например, может возникнуть желание отсортировать комплексные числа как по абсолютному значению, так и по вещественной части. Это можно сделать, определив два класса операторов для типа данных и выбрав подходящий класс при создании индекса. За дополнительными сведениями о классах операторов обратитесь к Разделу 11.10 и Разделу 38.16.
Когда команда CREATE INDEX вызывается для секционированной таблицы, по умолчанию её действие распространятся рекурсивно на все секции, с тем чтобы в них оказались соответствующие индексы. Сначала каждая секция проверяется на наличие равнозначного индекса, и, если таковой находится, он присоединяется как индекс секции к создаваемому, который таким образом становится родительским индексом. Если нужного индекса не оказывается, новый индекс автоматически создаётся и присоединяется к основному; имя индекса для каждой секции выбирается так же, как и при выполнении этой команды без имени индекса. С указанием ONLY рекурсия не производится и индекс помечается как нерабочий. (Команда ALTER INDEX ... ATTAC