W którym momencie baza danych aktualizuje swoje indeksy w transakcji?

11

Próbuję zrozumieć sekwencję zdarzeń we wstawkach, w które zaangażowany jest zarówno indeks, jak i transakcja.

Na przykład dokumentacja Oracle stwierdza:

Jeśli utworzysz [lub masz] jeden lub więcej indeksów przed załadowaniem danych, baza danych musi aktualizować każdy indeks, gdy wstawiany jest każdy wiersz.

Ale co się stanie, jeśli utworzę transakcję, wstawię pięć wierszy, a następnie zatwierdzę? Czy indeksy są aktualizowane dla każdej wstawki, czy tylko w punkcie zatwierdzenia?

Logika mówi mi, że byłyby one aktualizowane tylko w punkcie zatwierdzenia, ponieważ zaktualizowany indeks nie mógłby być przydatny do momentu zatwierdzenia tych rekordów. Ale czy to prawda?

Jeśli tak, kiedy mam 1 milion wierszy do wstawienia, dla najlepszej wydajności powinienem wykonać jedno duże zatwierdzenie ze wszystkich wierszy, a nie 10 transakcji na 100 000 rekordów? Oczywiście zdaję sobie sprawę, że ryzyko to będzie większe, jeśli wiersz 999,999 ulegnie awarii.

Przepraszam, jeśli moja terminologia jest trochę na wyczerpaniu. Z zawodu nie jestem DBA. Nie jestem tak bardzo zainteresowany konkretną bazą danych, jak ogólnie bazami danych, chociaż Oracle i Postgres są tym, z czego najczęściej korzystam. Szukałem tego tematu, ale tak naprawdę nie mogę znaleźć ostatecznej odpowiedzi.

Mark Ireland
źródło

Odpowiedzi:

8

Pracuję z SQL Server i Oracle. Prawdopodobnie istnieją pewne wyjątki, ale w przypadku tych platform ogólną odpowiedzią jest to, że dane i indeksy będą aktualizowane w tym samym czasie.

Myślę, że warto byłoby rozróżnić, kiedy indeksy są aktualizowane dla sesji będącej właścicielem transakcji i dla innych sesji. Domyślnie inne sesje nie zobaczą zaktualizowanych indeksów, dopóki transakcja nie zostanie zatwierdzona. Jednak sesja będąca właścicielem transakcji natychmiast zobaczy zaktualizowane indeksy.

Aby zastanowić się nad tym sposobem, zastanów się przy stole z kluczem podstawowym. W SQL Server i Oracle jest to zaimplementowane jako indeks. Przez większość czasu chcemy, aby natychmiast wystąpił błąd, jeśli INSERTzostanie wykonane, co naruszałoby klucz podstawowy. Aby tak się stało, indeks musi zostać zaktualizowany w tym samym czasie co dane. Należy pamiętać, że inne platformy, takie jak Postgres, zezwalają na odroczone ograniczenia, które są sprawdzane tylko w momencie zatwierdzenia transakcji.

Oto szybkie demo Oracle pokazujące typowy przypadek:

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

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

Druga INSERTinstrukcja zgłasza błąd:

Błąd SQL: ORA-00001: Naruszono unikalne ograniczenie (XXXXXX.SYS_C00384850)

00001. 00000 - „Naruszono wyjątkowe ograniczenie (% s.% S)”

* Przyczyna: Instrukcja UPDATE lub INSERT próbowała wstawić duplikat klucza. W przypadku Trusted Oracle skonfigurowanego w trybie DBMS MAC może zostać wyświetlony ten komunikat, jeśli na innym poziomie istnieje zduplikowany wpis.

* Działanie: Usuń unikalne ograniczenie lub nie wkładaj klucza.

Jeśli wolisz, aby akcja aktualizacji indeksu poniżej była prostą wersją demonstracyjną w SQL Server. Najpierw utwórz tabelę z dwiema kolumnami z milionem wierszy i indeks nieklastrowany w VALkolumnie:

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);

Poniższe zapytanie może korzystać z indeksu nieklastrowanego, ponieważ indeks jest indeksem obejmującym to zapytanie. Zawiera wszystkie dane potrzebne do jego wykonania. Zgodnie z oczekiwaniami zwroty nie są zwracane.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

zapytanie 1

Teraz zacznijmy transakcję i zaktualizuj VALprawie wszystkie wiersze w tabeli:

BEGIN TRANSACTION

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

Oto część planu zapytań:

zapytanie 2

Zakreślona na czerwono aktualizacja indeksu nieklastrowanego. Zakreślona na niebiesko aktualizacja indeksu klastrowego, który jest zasadniczo danymi tabeli. Mimo że transakcja nie została zatwierdzona, widzimy, że dane i indeks są aktualizowane w ramach wykonywania zapytania. Pamiętaj, że nie zawsze zobaczysz to w planie, w zależności od wielkości danych i ewentualnie innych czynników.

Ponieważ transakcja nadal nie została zatwierdzona, wróćmy do SELECTzapytania z góry.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

wprowadź opis zdjęcia tutaj

Optymalizator zapytań nadal może korzystać z indeksu i tym razem szacuje, że zostanie zwróconych 999999 wierszy. Wykonanie zapytania zwraca oczekiwany wynik.

To było proste demo, ale mam nadzieję, że trochę to wyjaśniło.

Nawiasem mówiąc, zdaję sobie sprawę z kilku przypadków, w których można argumentować, że indeks nie jest natychmiast aktualizowany. Odbywa się to ze względu na wydajność, a użytkownik końcowy nie powinien widzieć niespójnych danych. Na przykład, czasami usunięcia nie zostaną w pełni zastosowane do indeksu w SQL Server. Proces w tle jest uruchamiany i ostatecznie czyści dane. Jeśli jesteś ciekawy, możesz przeczytać o zapisach o duchach .

Joe Obbish
źródło
To super odpowiedź - a także odpowiada na inną rzecz, nad którą się zastanawiałem: czy naruszenie klucza podstawowego (lub podobnego) nastąpiłoby we Insert lub Commit. Dzięki za tak kompletną odpowiedź.
Mark Ireland
Powiązane pytanie (o tym, kiedy nastąpi naruszenie ograniczenia) dotyczy tego, czy korzystasz z odroczonych transakcji, czy nie. Na przykład SQL Server nie wdrożył odroczonej transakcji, więc wszystkie naruszenia mają miejsce na końcu instrukcji. Inne DBMS mają (na przykład Postgres, chociaż nie dla wszystkich rodzajów ograniczeń), więc gdy ograniczenie zostanie odroczone, naruszenie zostanie sprawdzone na etapie zatwierdzania transakcji).
ypercubeᵀᴹ
Oracle obsługuje również odroczone ograniczenia
BobC
1

Z mojego doświadczenia wynika, że ​​1 000 000 wstawień wierszy będzie wymagało więcej zasobów i zajmie więcej czasu niż w przypadku użycia wstawek wsadowych. Można to za przykład zaimplementować w 100 wstawkach po 10 000 wierszy.

Zmniejsza to narzut wstawianych partii, a jeśli partia się nie powiedzie, jest to mniejsze wycofanie.

W każdym razie dla SQL Server istnieje narzędzie bcp lub polecenie BULK INSERT, które można wykorzystać do wstawiania wsadowego.

I oczywiście możesz również zaimplementować własny kod do obsługi tego podejścia.

RLF
źródło
1
Ogólnie rzecz biorąc, jeśli chcesz wstawić dużą liczbę wierszy do tabeli, która wymaga indeksu, prawdopodobnie szybciej będzie upuścić indeks, załadować dane, a następnie odbudować indeks. Oracle obsługuje również opcję ładowania masowego ścieżki bezpośredniej, używając podpowiedzi / * + APPEND * /.
BobC