Poziomy izolacji i konflikty zapisu w usłudze Azure Databricks

Poziom izolacji tabeli definiuje stopień, w jakim transakcja musi być odizolowana od modyfikacji wprowadzonych przez operacje współbieżne. Konflikty zapisu w usłudze Azure Databricks zależą od poziomu izolacji.

Usługa Delta Lake zapewnia gwarancje transakcji ACID między odczytami i zapisami. Oznacza to, że:

  • Wiele składników zapisywania w wielu klastrach może jednocześnie modyfikować partycję tabeli. Autorzy widzą spójny widok migawki tabeli i zapisy występują w kolejności szeregowej.
    • Czytelnicy nadal widzą spójny widok migawki tabeli, z którą uruchomiono zadanie usługi Azure Databricks, nawet jeśli tabela jest modyfikowana podczas zadania.

Zobacz Co to są gwarancje ACID w usłudze Azure Databricks?.

Uwaga

Usługa Azure Databricks domyślnie używa usługi Delta Lake dla wszystkich tabel. W tym artykule opisano zachowanie usługi Delta Lake w usłudze Azure Databricks.

Ważne

Zmiany metadanych powodują niepowodzenie wszystkich współbieżnych operacji zapisu. Te operacje obejmują zmiany protokołu tabeli, właściwości tabeli lub schematu danych.

Operacje odczytu przesyłania strumieniowego kończą się niepowodzeniem, gdy napotkają zatwierdzenie, które zmienia metadane tabeli. Jeśli chcesz, aby strumień kontynuował, musisz uruchomić go ponownie. Aby zapoznać się z zalecanymi metodami, zobacz Zagadnienia dotyczące produkcji przesyłania strumieniowego ze strukturą.

Poniżej przedstawiono przykłady zapytań, które zmieniają metadane:

-- Set a table property.
ALTER TABLE table-name SET TBLPROPERTIES ('delta.isolationLevel' = 'Serializable')

-- Enable a feature using a table property and update the table protocol.
ALTER TABLE table_name SET TBLPROPERTIES ('delta.enableDeletionVectors' = true);

-- Drop a table feature.
ALTER TABLE table_name DROP FEATURE deletionVectors;

-- Upgrade to UniForm.
REORG TABLE table_name APPLY (UPGRADE UNIFORM(ICEBERG_COMPAT_VERSION=2));

-- Update the table schema.
ALTER TABLE table_name ADD COLUMNS (col_name STRING);

Konflikty zapisu ze współbieżnością na poziomie wiersza

Współbieżność na poziomie wiersza zmniejsza konflikty między współbieżnych operacji zapisu, wykrywając zmiany na poziomie wiersza i automatycznie rozwiązując konflikty występujące podczas współbieżnych operacji zapisu lub usuwania różnych wierszy w tym samym pliku danych.

Współbieżność na poziomie wiersza jest ogólnie dostępna w środowisku Databricks Runtime 14.2 lub nowszym. Współbieżność na poziomie wiersza jest domyślnie obsługiwana dla następujących warunków:

  • Tabele z wektorami usuwania są włączone i bez partycjonowania.
  • Tabele z klastrowaniem płynnym, chyba że wyłączono wektory usuwania.

Tabele z partycjami nie obsługują współbieżności na poziomie wiersza, ale nadal mogą unikać konfliktów między OPTIMIZE wszystkimi innymi operacjami zapisu po włączeniu wektorów usuwania. Zobacz Ograniczenia dotyczące współbieżności na poziomie wiersza.

Aby uzyskać informacje o innych wersjach środowiska Databricks Runtime, zobacz Zachowanie podglądu współbieżności na poziomie wiersza (starsza wersja).

W poniższej tabeli opisano, które pary operacji zapisu mogą powodować konflikty na każdym poziomie izolacji z włączonym współbieżnością na poziomie wiersza.

Uwaga

Tabele z kolumnami tożsamości nie obsługują transakcji współbieżnych. Zobacz Używanie kolumn tożsamości w usłudze Delta Lake.

INSERT (1) AKTUALIZOWANIE, USUWANIE, SCALANIE Z OPTIMIZE
INSERT Nie można powodować konfliktu
AKTUALIZOWANIE, USUWANIE, SCALANIE Z Nie można powodować konfliktu w funkcji WriteSerializable. Może powodować konflikt w przypadku serializacji podczas modyfikowania tego samego wiersza; zobacz Ograniczenia współbieżności na poziomie wiersza. MERGE INTO wymaga funkcji Photon na potrzeby rozwiązywania konfliktów na poziomie wiersza. Może powodować konflikt podczas modyfikowania tego samego wiersza; zobacz Ograniczenia współbieżności na poziomie wiersza.
OPTIMIZE Nie można powodować konfliktu Nie można powodować konfliktu Nie można powodować konfliktu

Ważne

(1) Wszystkie INSERT operacje w powyższych tabelach opisują operacje dołączania, które nie odczytują żadnych danych z tej samej tabeli przed zatwierdzeniem. INSERT operacje zawierające podzapytania odczytu tej samej tabeli obsługują tę samą współbieżność co MERGE.

Konflikty zapisu bez współbieżności na poziomie wiersza

W poniższej tabeli opisano, które pary operacji zapisu mogą powodować konflikty na każdym poziomie izolacji.

Tabele nie obsługują współbieżności na poziomie wiersza, jeśli mają zdefiniowane partycje lub nie mają włączonych wektorów usuwania. Środowisko Databricks Runtime 14.2 lub nowsze jest wymagane do współbieżności na poziomie wiersza.

Uwaga

Tabele z kolumnami tożsamości nie obsługują transakcji współbieżnych. Zobacz Używanie kolumn tożsamości w usłudze Delta Lake.

INSERT (1) AKTUALIZOWANIE, USUWANIE, SCALANIE Z OPTIMIZE
INSERT Nie można powodować konfliktu
AKTUALIZOWANIE, USUWANIE, SCALANIE Z Nie można powodować konfliktu w funkcji WriteSerializable. Może powodować konflikt z możliwością serializacji; zobacz Unikanie konfliktów z partycjami. Może powodować konflikty z możliwością serializacji i zapisuSerializowalne; zobacz Unikanie konfliktów z partycjami.
OPTIMIZE Nie można powodować konfliktu Nie można powodować konfliktu z tabelami z włączonymi wektorami usuwania. W przeciwnym razie może powodować konflikt. Nie można powodować konfliktu z tabelami z włączonymi wektorami usuwania. W przeciwnym razie może powodować konflikt.

Ważne

(1) Wszystkie INSERT operacje w powyższych tabelach opisują operacje dołączania, które nie odczytują żadnych danych z tej samej tabeli przed zatwierdzeniem. INSERT operacje zawierające podzapytania odczytu tej samej tabeli obsługują tę samą współbieżność co MERGE.

Ograniczenia współbieżności na poziomie wiersza

Niektóre ograniczenia dotyczą współbieżności na poziomie wiersza. W przypadku następujących operacji rozwiązywanie konfliktów jest zgodne z normalną współbieżnością konfliktów zapisu w usłudze Azure Databricks. Zobacz Konflikty zapisu bez współbieżności na poziomie wiersza.

  • OPTIMIZE polecenia za pomocą ZORDER BYpolecenia .
  • Polecenia ze złożonymi klauzulami warunkowymi, w tym następujące:
    • Warunki dotyczące złożonych typów danych, takich jak struktury, tablice lub mapy.
    • Warunki używające wyrażeń niedeterministycznych i podzapytania.
    • Warunki zawierające skorelowane podzapytania.
  • W przypadku MERGE poleceń należy użyć jawnego predykatu w tabeli docelowej, aby filtrować wiersze pasujące do tabeli źródłowej. W przypadku rozpoznawania scalania filtr służy tylko do skanowania wierszy, które mogą powodować konflikty na podstawie warunków filtrowania w operacjach współbieżnych.

Uwaga

Wykrywanie konfliktów na poziomie wiersza może zwiększyć całkowity czas wykonywania. W przypadku wielu równoczesnych transakcji składnik zapisywania określa priorytet opóźnienia w przypadku rozwiązywania konfliktów i konfliktów.

Obowiązują również wszystkie ograniczenia dotyczące wektorów usuwania. Zobacz Ograniczenia.

Kiedy usługa Delta Lake zatwierdza bez odczytywania tabeli?

Operacje usługi Delta Lake INSERT lub dołączania nie odczytują stanu tabeli przed zatwierdzeniem, jeśli spełnione są następujące warunki:

  1. Logika jest wyrażana przy użyciu INSERT logiki SQL lub trybu dołączania.
  2. Logika nie zawiera podzapytania ani warunkowych odwołujących się do tabeli objętej operacją zapisu.

Podobnie jak w przypadku innych zatwierdzeń usługa Delta Lake weryfikuje i rozpoznaje wersje tabel dotyczące zatwierdzenia przy użyciu metadanych w dzienniku transakcji, ale żadna wersja tabeli nie jest w rzeczywistości odczytywana.

Uwaga

Wiele typowych wzorców używa MERGE operacji do wstawiania danych na podstawie warunków tabeli. Chociaż może być możliwe ponowne zapisywanie tej logiki przy użyciu INSERT instrukcji, jeśli dowolne wyrażenie warunkowe odwołuje się do kolumny w tabeli docelowej, te instrukcje mają takie same ograniczenia współbieżności jak MERGE.

Zapisuj możliwe do serializacji i możliwe do serializacji poziomy izolacji

Poziom izolacji tabeli definiuje stopień, w jakim transakcja musi być odizolowana od modyfikacji wprowadzonych przez transakcje współbieżne. Usługa Delta Lake w usłudze Azure Databricks obsługuje dwa poziomy izolacji: Serializable i WriteSerializable.

  • Możliwe do serializacji: najsilniejszy poziom izolacji. Gwarantuje to, że zatwierdzone operacje zapisu i wszystkie operacje odczytu są serializowalne. Operacje są dozwolone tak długo, jak istnieje sekwencja szeregowa wykonywania ich jednorazowo, która generuje taki sam wynik jak w tabeli. W przypadku operacji zapisu sekwencja szeregowa jest dokładnie taka sama jak w historii tabeli.

  • WriteSerializable (wartość domyślna): słabszy poziom izolacji niż serializowalny. Gwarantuje to tylko, że operacje zapisu (czyli nie odczyty) można serializować. Jest to jednak nadal silniejsze niż izolacja migawki . WriteSerializable jest domyślnym poziomem izolacji, ponieważ zapewnia doskonałą równowagę spójności danych i dostępności dla najbardziej typowych operacji.

    W tym trybie zawartość tabeli delty może się różnić od oczekiwanej od sekwencji operacji widocznych w historii tabeli. Jest to spowodowane tym, że ten tryb umożliwia wykonywanie niektórych par współbieżnych zapisów (np. operacji X i Y), tak aby wynik był taki, jakby Y został wykonany przed X (czyli serializowalny między nimi), mimo że historia wykazała, że Y zostało zatwierdzone po X. Aby nie zezwalać na zmienianie kolejności, ustaw poziom izolacji tabeli, aby można było serializować, aby spowodować niepowodzenie tych transakcji.

Operacje odczytu zawsze używają izolacji migawki. Poziom izolacji zapisu określa, czy czytelnik może zobaczyć migawkę tabeli, że zgodnie z historią "nigdy nie istniał".

W przypadku poziomu z możliwością serializacji czytelnik zawsze widzi tylko tabele zgodne z historią. Na poziomie WriteSerializable czytelnik może zobaczyć tabelę, która nie istnieje w dzienniku delty.

Rozważmy na przykład txn1, długotrwałą operację usuwania i txn2, która wstawia dane usunięte przez txn1. txn2 i txn1 zakończyć i są one rejestrowane w tej kolejności w historii. Zgodnie z historią dane wstawione w txn2 nie powinny istnieć w tabeli. W przypadku poziomu z możliwością serializacji czytnik nigdy nie widziałby danych wstawionych przez txn2. Jednak na poziomie WriteSerializable czytnik może w pewnym momencie zobaczyć dane wstawione przez txn2.

Aby uzyskać więcej informacji na temat typów operacji, które mogą powodować konflikty ze sobą na każdym poziomie izolacji i możliwych błędów, zobacz Unikanie konfliktów przy użyciu partycjonowania i rozłącznych warunków poleceń.

Ustawianie poziomu izolacji

Poziom izolacji można ustawić przy użyciu ALTER TABLE polecenia .

ALTER TABLE <table-name> SET TBLPROPERTIES ('delta.isolationLevel' = <level-name>)

gdzie <level-name> to Serializable lub WriteSerializable.

Aby na przykład zmienić poziom izolacji z domyślnego WriteSerializable na Serializable, uruchom polecenie:

ALTER TABLE <table-name> SET TBLPROPERTIES ('delta.isolationLevel' = 'Serializable')

Unikaj konfliktów przy użyciu partycjonowania i rozłącznych warunków poleceń

We wszystkich przypadkach oznaczonych jako "może powodować konflikt", czy te dwie operacje będą powodować konflikt, zależy od tego, czy działają one na tym samym zestawie plików. Dwa zestawy plików można rozdzielić, partycjonując tabelę według tych samych kolumn, które są używane w warunkach operacji. Na przykład dwa polecenia UPDATE table WHERE date > '2010-01-01' ... i DELETE table WHERE date < '2010-01-01' będą powodować konflikt, jeśli tabela nie jest partycjonowana według daty, ponieważ obie mogą próbować zmodyfikować ten sam zestaw plików. Partycjonowanie tabeli przez date spowoduje uniknięcie konfliktu. W związku z tym partycjonowanie tabeli zgodnie z warunkami często używanymi w poleceniu może znacznie zmniejszyć konflikty. Jednak partycjonowanie tabeli według kolumny o wysokiej kardynalności może prowadzić do innych problemów z wydajnością z powodu dużej liczby podkatalogów.

Wyjątki konfliktów

Po wystąpieniu konfliktu transakcji wystąpi jeden z następujących wyjątków:

ConcurrentAppendException

Wyjątek ten występuje, gdy współbieżna operacja dodaje pliki w tej samej partycji (lub w dowolnym miejscu w niepartycjonowanej tabeli), którą odczytuje operacja. Dodatki plików mogą być spowodowane operacjami INSERT, DELETE, UPDATElub MERGE .

Przy domyślnym poziomie WriteSerializable izolacji plików dodanych przez operacje ślepeINSERT (czyli operacje, które ślepo dołączają dane bez odczytywania żadnych danych) nie powodują konfliktu z żadną operacją, nawet jeśli dotykają tej samej partycji (lub w dowolnym miejscu w niepartowanej tabeli). Jeśli poziom izolacji jest ustawiony na Serializablewartość , dołączanie ślepe może powodować konflikt.

Ten wyjątek jest często zgłaszany podczas współbieżnych DELETEoperacji , UPDATElub MERGE . Podczas gdy operacje współbieżne mogą fizycznie aktualizować różne katalogi partycji, jedna z nich może odczytywać tę samą partycję, którą druga współbieżnie aktualizuje, powodując w ten sposób konflikt. Możesz tego uniknąć, wyraźnie zaznaczając separację w warunku operacji. Rozważmy następujący przykład.

// Target 'deltaTable' is partitioned by date and country
deltaTable.as("t").merge(
    source.as("s"),
    "s.user_id = t.user_id AND s.date = t.date AND s.country = t.country")
  .whenMatched().updateAll()
  .whenNotMatched().insertAll()
  .execute()

Załóżmy, że uruchamiasz powyższy kod współbieżnie dla różnych dat lub krajów. Z uwagi na fakt, że każde zadanie działa na niezależnej partycji docelowej tabeli Delta, nie należy spodziewać się żadnych konfliktów. Jednak warunek nie jest wystarczająco wyraźny i może skanować całą tabelę i może kolidować z współbieżnymi operacjami aktualizującymi inne partycje. Zamiast tego możesz przepisać instrukcję, aby dodać określoną datę i kraj do warunku scalania, jak pokazano w poniższym przykładzie.

// Target 'deltaTable' is partitioned by date and country
deltaTable.as("t").merge(
    source.as("s"),
    "s.user_id = t.user_id AND s.date = t.date AND s.country = t.country AND t.date = '" + <date> + "' AND t.country = '" + <country> + "'")
  .whenMatched().updateAll()
  .whenNotMatched().insertAll()
  .execute()

Operacja ta może być teraz bezpiecznie przeprowadzana współbieżnie w różnych terminach i krajach.

ConcurrentDeleteReadException

Ten wyjątek występuje, gdy operacja współbieżna usunęła plik odczytany przez operację. Typowe przyczyny to DELETEoperacja , UPDATElub MERGE , która ponownie zapisuje pliki.

ConcurrentDeleteDeleteException

Ten wyjątek występuje, gdy operacja współbieżna usunęła plik, który operacja również usuwa. Może to być spowodowane przez dwie współbieżne operacje kompaktowania przepisujące te same pliki.

MetadataChangedException

Ten wyjątek występuje, gdy współbieżna transakcja aktualizuje metadane tabeli delty. Typowe przyczyny to ALTER TABLE operacje lub zapisy w tabeli delty, które aktualizują schemat tabeli.

ConcurrentTransactionException

Jeśli zapytanie przesyłane strumieniowo przy użyciu tej samej lokalizacji punktu kontrolnego jest uruchamiane wiele razy jednocześnie i próbuje zapisać w tabeli delty w tym samym czasie. Dwa zapytania strumieniowe nigdy nie powinny korzystać z tej samej lokalizacji punktu kontrolnego i działać w tym samym czasie.

ProtocolChangedException

Ten wyjątek może wystąpić w następujących przypadkach:

  • Po uaktualnieniu tabeli delty do nowej wersji protokołu. Aby przyszłe operacje zakończyły się powodzeniem, może być konieczne uaktualnienie środowiska Databricks Runtime.
  • Gdy wielu pisarzy tworzy lub zastępuje tabelę w tym samym czasie.
  • Gdy wielu pisarzy pisze do pustej ścieżki w tym samym czasie.

Aby uzyskać więcej informacji, zobacz Jak usługa Azure Databricks zarządza zgodnością funkcji usługi Delta Lake?

Zachowanie podglądu współbieżności na poziomie wiersza (starsza wersja)

W tej sekcji opisano zachowania podglądu współbieżności na poziomie wiersza w środowisku Databricks Runtime 14.1 lub nowszym. Współbieżność na poziomie wiersza zawsze wymaga wektorów usuwania.

W środowisku Databricks Runtime 13.3 LTS lub nowszym tabele z włączonym klastrowaniem płynnym automatycznie włączają współbieżność na poziomie wiersza.

W środowisku Databricks Runtime 14.0 i 14.1 można włączyć współbieżność na poziomie wiersza dla tabel z wektorami usuwania, ustawiając następującą konfigurację klastra lub sparkSession:

spark.databricks.delta.rowLevelConcurrencyPreview = true

W środowisku Databricks Runtime 14.1 lub nowszym obliczenia inne niż photon obsługują tylko współbieżność na poziomie wiersza dla DELETE operacji.