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).
MERGE INTO
Obsługa współbieżności na poziomie wiersza wymaga aplikacji Photon w środowisku Databricks Runtime 14.2. W środowisku Databricks Runtime 14.3 LTS i nowszym aplikacja Photon nie jest wymagana.
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 dotyczące współbieżności na poziomie wiersza. | Może powodować konflikt podczas modyfikowania tego samego wiersza. Zobacz Ograniczenia dotyczące współbieżności na poziomie wiersza. | |
OPTIMIZE | Nie można powodować konfliktu | Może powodować konflikt, gdy ZORDER BY jest używany. Nie można powodować konfliktu w przeciwnym razie. |
Może powodować konflikt, gdy ZORDER BY jest używany. Nie można powodować konfliktu w przeciwnym razie. |
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
.
REORG
operacje mają semantyka izolacji identyczna jak OPTIMIZE
podczas ponownego zapisywania plików danych w celu odzwierciedlenia zmian zarejestrowanych w wektorach usuwania. W przypadku zastosowania REORG
uaktualnienia protokoły tabeli zmieniają się, co powoduje konflikt ze wszystkimi trwającymi operacjami.
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ć konflikt z możliwością serializacji i funkcji WriteSerializable. Zobacz Unikanie konfliktów z partycjami. | |
OPTIMIZE | Nie można powodować konfliktu | Nie można powodować konfliktu z tabelami z włączonymi wektorami usuwania, chyba że ZORDER BY jest używany. W przeciwnym razie może powodować konflikt. |
Nie można powodować konfliktu z tabelami z włączonymi wektorami usuwania, chyba że ZORDER BY jest używany. 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
.
REORG
operacje mają semantyka izolacji identyczna jak OPTIMIZE
podczas ponownego zapisywania plików danych w celu odzwierciedlenia zmian zarejestrowanych w wektorach usuwania. W przypadku zastosowania REORG
uaktualnienia protokoły tabeli zmieniają się, co powoduje konflikt ze wszystkimi trwającymi operacjami.
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.
- 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 środowisku Databricks Runtime 14.2
MERGE
polecenia muszą używać jawnego predykatu w tabeli docelowej do filtrowania wierszy pasujących do tabeli źródłowej. W przypadku rozpoznawania scalania filtr skanuje tylko wiersze, które mogą powodować konflikt 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:
- Logika jest wyrażana przy użyciu
INSERT
logiki SQL lub trybu dołączania. - 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
, UPDATE
lub MERGE
.
Przy domyślnym poziomie WriteSerializable
izolacji plików dodanych przez operacje ślepe INSERT
(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 Serializable
wartość , dołączanie ślepe może powodować konflikt.
Ten wyjątek jest często zgłaszany podczas współbieżnych DELETE
operacji , UPDATE
lub 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 DELETE
operacja , UPDATE
lub 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.