61.5. Проверка уникальности индекса#
61.5. Проверка уникальности индекса #
Tantor SE-1C применяет ограничения уникальности SQL с помощью уникальных индексов, которые являются индексами, запрещающими наличие нескольких записей с одинаковыми ключами. Метод доступа, поддерживающий эту функцию, устанавливает значение amcanunique
в true.
(На данный момент только b-tree поддерживает это). Столбцы, перечисленные в предложении INCLUDE
, не учитываются при обеспечении уникальности.
Из-за MVCC всегда необходимо разрешать физическое существование дублирующих записей в индексе: эти записи могут ссылаться на последовательные версии одной логической строки. Фактическое поведение, которое мы хотим обеспечить, заключается в том, что ни одна MVCC снимок не может содержать две строки с одинаковыми ключами индекса. Это разбивается на следующие случаи, которые должны быть проверены при вставке новой строки в уникальный индекс:
Если конфликтующая допустимая строка была удалена текущей транзакцией, это нормально. (В частности, поскольку операция UPDATE всегда удаляет старую версию строки перед вставкой новой версии, это позволит выполнить операцию UPDATE на строке без изменения ключа).
Если конфликтующая строка была вставлена еще не подтвержденной транзакцией, вставляющая транзакция должна ждать, чтобы увидеть, подтверждается ли эта транзакция. Если она откатывается, то конфликта нет. Если она подтверждается, не удаляя конфликтующую строку, возникает нарушение уникальности. (На практике мы просто ждем завершения другой транзакции и затем повторяем проверку видимости полностью).
Аналогично, если конфликтующая допустимая строка была удалена еще не подтвержденной транзакцией, потенциальный вставщик должен ждать коммита или отмены этой транзакции, а затем повторить тест.
Кроме того, непосредственно перед сообщением о нарушении уникальности в соответствии с вышеуказанными правилами, метод доступа должен повторно проверить активность вставляемой строки. Если она является зафиксированно удаленной, то нарушение не должно быть сообщено. (Этот случай не может возникнуть во время обычного сценария вставки строки, которая только что была создана текущей транзакцией. Однако это может произойти во время CREATE UNIQUE INDEX CONCURRENTLY
).
Мы требуем, чтобы сам метод доступа к индексу применял эти тесты, что означает, что он должен обратиться к куче, чтобы проверить статус коммита любой строки, которая, согласно содержимому индекса, имеет дублирующийся ключ. Это, безусловно, некрасиво и не модульно, но это позволяет избежать избыточной работы: если бы мы делали отдельный запрос, то поиск индекса для конфликтующей строки фактически повторялся бы при поиске места для вставки новой записи индекса. Более того, нет очевидного способа избежать гонок, если проверка конфликта не является неотъемлемой частью вставки новой записи индекса.
Если ограничение уникальности может быть отложенным, возникает дополнительная сложность: нам нужно иметь возможность вставить запись в индекс для новой строки, но отложить любую ошибку нарушения уникальности до конца оператора или даже позже. Чтобы избежать лишних повторных поисков в индексе, метод доступа к индексу должен выполнить предварительную проверку уникальности во время начальной вставки. Если это показывает, что определенно нет конфликтующей активной кортежи, мы закончили. В противном случае мы планируем повторную проверку, которая будет выполняться, когда придет время применить ограничение. Если во время повторной проверки как вставленный кортеж, так и другой кортеж с тем же ключом являются активными, то ошибка должна быть сообщена. (Обратите внимание, что для этой цели термин "активный" на самом деле означает "любой кортеж в цепочке HOT-кортежей записи в индексе".) Для реализации этого функции aminsert
передается параметр checkUnique
, который может иметь одно из следующих значений:
UNIQUE_CHECK_NO
указывает, что проверка уникальности не должна выполняться (это не уникальный индекс).UNIQUE_CHECK_YES
указывает, что это неоткладываемый уникальный индекс, и проверка уникальности должна быть выполнена немедленно, как описано выше.UNIQUE_CHECK_PARTIAL
указывает, что ограничение уникальности может быть отложено. Tantor SE-1C будет использовать этот режим для вставки записей в индекс каждой строки. Метод доступа должен позволять дублирующиеся записи в индексе и сообщать о возможных дубликатах, возвращая false из функцииaminsert
. Для каждой строки, для которой возвращается false, будет запланировано отложенное повторное проверка.Метод доступа должен идентифицировать любые строки, которые могут нарушить ограничение уникальности, но это не является ошибкой, если он сообщает о ложных положительных результатах. Это позволяет выполнить проверку без ожидания завершения других транзакций; конфликты, сообщенные здесь, не считаются ошибками и будут повторно проверены позже, к тому времени они могут уже не являться конфликтами.
UNIQUE_CHECK_EXISTING
указывает, что это отложенная проверка строки, которая была сообщена как потенциальное нарушение уникальности. Хотя это реализуется путем вызоваaminsert
, метод доступа не должен вставлять новую запись индекса в этом случае. Запись индекса уже присутствует. Вместо этого метод доступа должен проверить, есть ли другая активная запись индекса. Если да, и если целевая строка также все еще активна, сообщить об ошибке.Советуется, чтобы в вызове
UNIQUE_CHECK_EXISTING
метод доступа также проверял, что целевая строка действительно имеет существующую запись в индексе, и в случае отсутствия такой записи сообщал об ошибке. Это хорошая идея, потому что значения кортежей индекса, переданные вaminsert
, были пересчитаны. Если определение индекса включает функции, которые на самом деле не являются постоянными, можно проверять неправильную область индекса. Проверка того, что целевая строка найдена при повторной проверке, подтверждает, что мы сканируем те же значения кортежей, которые использовались при первоначальной вставке.