У який момент база даних оновлює свої індекси в транзакції?


11

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

Наприклад, документація Oracle зазначає:

Якщо ви створюєте [або маєте] один або більше індексів перед завантаженням даних, тоді база даних повинна оновлювати кожен індекс, як вставляється кожен рядок.

Але що станеться, якщо я створять транзакцію, вставляю п'ять рядків, а потім здійснюю? Чи оновлюються індекси для кожної вставки чи просто в точці фіксації?

Логіка підказує мені, що вони будуть оновлюватися лише в момент фіксації, оскільки оновлений індекс не міг би бути корисним до тих пір, поки ці записи не будуть скоєні. Але це правда?

Якщо так, коли я маю вставити 1м рядків, для кращої продуктивності я повинен зробити одне велике фіксування всіх рядків, а не 10 транзакцій із записом 100k? Звичайно, я розумію, що це загрожує більшим відказом, якщо рядок 999,999 не вдасться.

Вибачте, якщо моя термінологія трохи не виходить. Я не DBA в галузі торгівлі. Мене не так цікавить конкретна база даних, як загалом бази даних, хоча 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 намагалася вставити повторюваний ключ. Для довіреного 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. Запускається фоновий процес і з часом очищає дані. Ви можете прочитати про записи про привидів, якщо вам цікаво.


Це чудова відповідь - а також відповідає ще одне, що мені було цікаво: чи буде порушення в первинному ключі (або подібному) на Insert або на Commit. Дякую за таку повну відповідь.
Марк Ірландії

Пов'язане запитання (про те, коли відбудеться порушення обмеження) пов’язане з тим, використовуєте ви відкладені транзакції чи ні. Наприклад, SQL Server не реалізував відкладені транзакції, тому всі порушення трапляються в кінці висловлювань. Інші СУБД мають (наприклад, Postgres, хоча не для всіх типів обмежень), тому коли ви обмежуєте відстрочку, порушення буде перевірено на фазі фіксації транзакції).
ypercubeᵀᴹ

Oracle також підтримує відкладені обмеження
BobC

1

Мій досвід полягає в тому, що для вставки 1000 000 рядків фактично буде потрібно більше ресурсів і потрібно більше часу, ніж якщо ви використовували пакетні вставки. Це може бути реалізовано, як приклад, у 100 вставок з 10 000 рядків.

Це зменшує накладні витрати вставлених партій, і, якщо партія не працює, це менший відкат.

У будь-якому випадку для SQL Server є утиліта bcp або команда BULK INSERT, яка може використовуватися для створення пакетних вставок.

І, звичайно, ви також можете реалізувати власний код для керування цим підходом.


1
Загалом, якщо вам потрібно вставити велику кількість рядків у таблицю, якій потрібен індекс, швидше за все буде швидше скинути індекс, завантажити дані та потім відновити індекс. Oracle також підтримує опцію прямого об'ємного навантаження прямого шляху, використовуючи підказку / * + ДОДАТОК /.
BobC
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.