В какой момент база данных обновляет свои индексы в транзакции?


11

Я пытаюсь понять последовательность событий во вставках, где участвуют как индекс, так и транзакция.

Например, документация Oracle гласит:

Если вы создаете [или имеете] один или несколько индексов перед загрузкой данных, база данных должна обновлять каждый индекс по мере вставки каждой строки.

Но что произойдет, если я создаю транзакцию, вставляю пять строк, а затем фиксирую? Обновляются ли индексы для каждой вставки или только в момент фиксации?

Логика говорит мне, что они будут обновлены только в момент фиксации, потому что обновленный индекс не может быть полезен, пока эти записи не будут зафиксированы. Но так ли это?

Если так, когда у меня есть 1m строк для вставки, для лучшей производительности я должен сделать один большой коммит из всех строк, а не 10 транзакций из 100k записей? Конечно, я понимаю, что это рискует сделать больший откат, если строка 999,999 потерпит неудачу.

Извиняюсь, если моя терминология немного вышла. Я не адвокат по профессии. Меня интересует не столько конкретная база данных, сколько базы данных в целом, хотя Oracle и Postgres - это то, что я использую больше всего. Я искал по этой теме, но не могу найти окончательного ответа.

Ответы:


8

Я работаю с SQL Server и Oracle. Возможно, есть некоторые исключения, но для этих платформ общий ответ заключается в том, что данные и индексы будут обновляться одновременно.

Я думаю, что было бы полезно провести различие между обновлением индексов для сеанса, которому принадлежит транзакция, и для других сеансов. По умолчанию другие сеансы не будут видеть обновленные индексы, пока транзакция не будет зафиксирована. Однако сеанс, которому принадлежит транзакция, сразу увидит обновленные индексы.

Для одного способа думать об этом, рассмотрим за столом с первичным ключом. В SQL Server и Oracle это реализовано как индекс. Большую часть времени мы хотим, чтобы немедленно возникала ошибка, если INSERTсделано, что нарушило бы первичный ключ. Чтобы это произошло, индекс должен обновляться одновременно с данными. Обратите внимание, что другие платформы, такие как Postgres, допускают отложенные ограничения, которые проверяются только при фиксации транзакции.

Вот краткая демонстрация Oracle, показывающая общий случай:

CREATE TABLE X_TABLE (PK INT NULL, PRIMARY KEY (PK));

INSERT INTO X_TABLE VALUES (1);
INSERT INTO X_TABLE VALUES (1); -- no commit

Второе INSERTутверждение выдает ошибку:

Ошибка SQL: ORA-00001: уникальное ограничение (XXXXXX.SYS_C00384850) нарушено

00001. 00000 - "уникальное ограничение (% s.% S) нарушено"

* Причина: оператор UPDATE или INSERT попытался вставить дубликат ключа. Для Trusted Oracle, настроенного в режиме MAC СУБД, вы можете увидеть это сообщение, если дублирующая запись существует на другом уровне.

* Действие: либо удалите уникальное ограничение, либо не вставляйте ключ.

Если вы предпочитаете видеть действие по обновлению индекса ниже, это простая демонстрация в SQL Server. Сначала создайте таблицу из двух столбцов с миллионом строк и некластеризованным индексом для VALстолбца:

DROP TABLE IF EXISTS X_TABLE_IX;

CREATE TABLE X_TABLE_IX (
ID INT NOT NULL,
VAL VARCHAR(10) NOT NULL
PRIMARY KEY (ID)
);

CREATE INDEX X_INDEX ON X_TABLE_IX (VAL);

-- insert one million rows with N from 1 to 1000000
INSERT INTO X_TABLE_IX
SELECT N, N FROM dbo.Getnums(1000000);

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

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

запрос 1

Теперь давайте начнем транзакцию и обновим VALпочти все строки в таблице:

BEGIN TRANSACTION

UPDATE X_TABLE_IX
SET VAL = 'A'
WHERE ID <> 1;

Вот часть плана запроса для этого:

запрос 2

Красным обведено кружочком обновление некластеризованного индекса. Обведено синим цветом - это обновление кластеризованного индекса, который по сути является данными таблицы. Даже если транзакция не была зафиксирована, мы видим, что данные и индекс обновляются в процессе выполнения запроса. Обратите внимание, что вы не всегда будете видеть это в плане в зависимости от объема данных, а также, возможно, от других факторов.

Поскольку транзакция еще не зафиксирована, давайте вернемся к SELECTзапросу сверху.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

введите описание изображения здесь

Оптимизатор запросов по-прежнему может использовать индекс, и на этот раз он оценивает, что будет возвращено 999999 строк. Выполнение запроса возвращает ожидаемый результат.

Это была простая демонстрация, но, надеюсь, это немного прояснило ситуацию.

Кроме того, я знаю несколько случаев, когда можно утверждать, что индекс не обновляется немедленно. Это сделано из соображений производительности, и конечный пользователь не должен видеть противоречивые данные. Например, иногда удаление не будет полностью применено к индексу в SQL Server. Фоновый процесс запускается и в конечном итоге очищает данные. Вы можете прочитать о призрачных записях, если вам интересно.


Это супер ответ, а также ответы на еще один вопрос, который меня интересовал: будет ли нарушение первичного ключа (или аналогичного) при вставке или при фиксации. Спасибо за такой полный ответ.
Марк Ирландия

Связанный вопрос (о том, когда произойдет нарушение ограничения) связан с тем, используете ли вы отложенные транзакции или нет. Например, в SQL Server не реализована отложенная транзакция, поэтому все нарушения происходят в конце операторов. Другие СУБД имеют (например, Postgres, хотя и не для всех типов ограничений), поэтому, когда вы откладываете ограничение, нарушение будет проверяться на этапе фиксации транзакции).
ypercubeᵀᴹ

Oracle также поддерживает отложенные ограничения
BobC

1

По моему опыту, для вставки в 1 000 000 строк потребуется больше ресурсов, и для их завершения потребуется больше времени, чем при использовании пакетной вставки. Это может быть реализовано, например, в 100 вставок по 10000 строк.

Это уменьшает накладные расходы на вставку пакетов и, если пакет завершается неудачей, это меньший откат.

В любом случае для SQL Server существует утилита bcp или команда BULK INSERT, которые можно использовать для пакетной вставки.

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


1
В общем, если вам нужно вставить большое количество строк в таблицу, для которой требуется индекс, скорее всего будет быстрее удалить индекс, загрузить данные и затем перестроить индекс. Oracle также поддерживает параметр массовой загрузки с прямым путем, используя подсказку / * + APPEND * /.
BobC
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.