Úrovně izolace a konflikty zápisu v Azure Databricks

Úroveň izolace tabulky definuje stupeň, do kterého musí být transakce izolovaná od úprav provedených souběžnými operacemi. Konflikty zápisu v Azure Databricks závisí na úrovni izolace.

Delta Lake poskytuje záruky transakcí ACID mezi čtením a zápisem. To znamená, že:

  • Oddíl tabulky může současně upravovat více zapisovačů ve více clusterech. Zapisovače vidí konzistentní zobrazení snímků tabulky a zápisů v sériovém pořadí.
    • Čtenáři i nadále uvidí konzistentní zobrazení snímku tabulky, se kterou začala úloha Azure Databricks, i když se během úlohy změní tabulka.

Podívejte se, co jsou záruky ACID v Azure Databricks?

Poznámka:

Azure Databricks ve výchozím nastavení používá Delta Lake pro všechny tabulky. Tento článek popisuje chování Delta Lake v Azure Databricks.

Důležité

Změny metadat způsobují selhání všech souběžných operací zápisu. Mezi tyto operace patří změny tabulkového protokolu, vlastností tabulky nebo schématu dat.

Čtení streamování selže, když narazí na potvrzení, které změní metadata tabulky. Pokud chcete, aby datový proud pokračoval, musíte ho restartovat. Doporučené metody najdete v tématu Důležité informace o produkčním prostředí pro strukturované streamování.

Tady jsou příklady dotazů, které mění metadata:

-- 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 zápisu s souběžností na úrovni řádků

Souběžnost na úrovni řádků snižuje konflikty mezi souběžnými operacemi zápisu tím, že detekuje změny na úrovni řádků a automaticky řeší konflikty, ke kterým dochází při souběžné aktualizaci zápisu nebo odstranění různých řádků ve stejném datovém souboru.

Souběžnost na úrovni řádků je obecně dostupná pro Databricks Runtime 14.2 a vyšší. Souběžnost na úrovni řádků je ve výchozím nastavení podporovaná pro následující podmínky:

  • Tabulky s povolenými vektory odstranění a bez dělení
  • Tabulky s clusteringem liquid, pokud jste nezablokovali vektory odstranění.

Tabulky s oddíly nepodporují souběžnost na úrovni řádků, ale mohou se vyhnout konfliktům mezi OPTIMIZE a všemi ostatními operacemi zápisu při povolení vektorů odstranění. Viz Omezení souběžnosti na úrovni řádků.

Další verze Modulu runtime Databricks najdete v tématu Chování souběžnosti na úrovni řádků (starší verze).

Následující tabulka popisuje, které páry operací zápisu můžou kolidovat v každé úrovni izolace s povolenou souběžností na úrovni řádků.

Poznámka:

Tabulky se sloupci identit nepodporují souběžné transakce. Viz Použití sloupců identit v Delta Lake.

INSERT (1) AKTUALIZOVAT, ODSTRANIT, SLOUČIT DO OPTIMIZE
INSERT Nejde v konfliktu
AKTUALIZOVAT, ODSTRANIT, SLOUČIT DO Nelze konfliktovat v writeSerializable. Může kolidovat v serializovatelné při úpravě stejného řádku; Viz Omezení souběžnosti na úrovni řádků. MERGE INTO vyžaduje Photon pro řešení konfliktů na úrovni řádků. Může dojít ke konfliktu při úpravě stejného řádku; Viz Omezení souběžnosti na úrovni řádků.
OPTIMIZE Nejde v konfliktu Nejde v konfliktu Nejde v konfliktu

Důležité

(1) Všechny INSERT operace v tabulkách výše popisují operace připojení, které před potvrzením nepřečtou žádná data ze stejné tabulky. INSERT operace, které obsahují poddotazy, které čtou stejnou tabulku, podporují stejnou souběžnost jako MERGE.

Konflikty zápisu bez souběžnosti na úrovni řádků

Následující tabulka popisuje, které páry operací zápisu můžou kolidovat v jednotlivých úrovních izolace.

Tabulky nepodporují souběžnost na úrovni řádků, pokud mají definované oddíly nebo nemají povolené vektory odstranění. Databricks Runtime 14.2 nebo vyšší se vyžaduje pro souběžnost na úrovni řádků.

Poznámka:

Tabulky se sloupci identit nepodporují souběžné transakce. Viz Použití sloupců identit v Delta Lake.

INSERT (1) AKTUALIZOVAT, ODSTRANIT, SLOUČIT DO OPTIMIZE
INSERT Nejde v konfliktu
AKTUALIZOVAT, ODSTRANIT, SLOUČIT DO Nelze konfliktovat v writeSerializable. Může kolidovat v Serializovatelné; vyhnete se konfliktům s oddíly. Může kolidovat v Serializovatelné a WriteSerializable; vyhnete se konfliktům s oddíly.
OPTIMIZE Nejde v konfliktu Nelze v konfliktu s tabulkami s povolenými vektory odstranění. Může být v konfliktu jinak. Nelze v konfliktu s tabulkami s povolenými vektory odstranění. Může být v konfliktu jinak.

Důležité

(1) Všechny INSERT operace v tabulkách výše popisují operace připojení, které před potvrzením nepřečtou žádná data ze stejné tabulky. INSERT operace, které obsahují poddotazy, které čtou stejnou tabulku, podporují stejnou souběžnost jako MERGE.

Omezení souběžnosti na úrovni řádků

Některá omezení platí pro souběžnost na úrovni řádků. Pro následující operace se řešení konfliktů řídí normální souběžností pro konflikty zápisu v Azure Databricks. Viz Konflikty zápisu bez souběžnosti na úrovni řádků.

  • OPTIMIZE příkazy s ZORDER BY.
  • Příkazy se složitými podmíněnými klauzulemi, včetně následujících:
    • Podmínky pro komplexní datové typy, jako jsou struktury, pole nebo mapy.
    • Podmínky používající ne deterministické výrazy a poddotazy
    • Podmínky, které obsahují korelované poddotazy.
  • Pro MERGE příkazy je nutné použít explicitní predikát cílové tabulky k filtrování řádků odpovídajících zdrojové tabulce. Pro řešení sloučení se filtr používá pouze ke kontrole řádků, které můžou kolidovat na základě podmínek filtru v souběžných operacích.

Poznámka:

Detekce konfliktů na úrovni řádků může zvýšit celkovou dobu provádění. V případě mnoha souběžných transakcí zapisovač upřednostňuje latenci při řešení konfliktů a může dojít ke konfliktům.

Platí také všechna omezení pro vektory odstranění. Viz Omezení.

Kdy se Delta Lake potvrdí bez čtení tabulky?

Operace Delta Lake INSERT nebo připojení před potvrzením nečtou stav tabulky, pokud jsou splněny následující podmínky:

  1. Logika se vyjadřuje pomocí INSERT logiky SQL nebo režimu připojení.
  2. Logika neobsahuje žádné poddotazy ani podmíněné výrazy, které odkazují na tabulku cílenou operací zápisu.

Stejně jako v jiných potvrzeních Delta Lake ověřuje a překládá verze tabulek při potvrzení pomocí metadat v transakčním protokolu, ale ve skutečnosti se nečte žádná verze tabulky.

Poznámka:

Mnoho běžných vzorů používá MERGE operace k vkládání dat na základě podmínek tabulky. I když může být možné přepsat tuto logiku pomocí INSERT příkazů, pokud některé podmíněné výrazy odkazují na sloupec v cílové tabulce, mají tyto příkazy stejná omezení souběžnosti jako MERGE.

Zápis serializovatelných vs. serializovatelných úrovní izolace

Úroveň izolace tabulky definuje stupeň, do kterého musí být transakce izolovaná od úprav provedených souběžnými transakcemi. Delta Lake v Azure Databricks podporuje dvě úrovně izolace: Serializovatelné a WriteSerializable.

  • Serializovatelné: Nejsilnější úroveň izolace. Zajišťuje, že potvrzené operace zápisu a všechna čtení jsou serializovatelná. Operace jsou povoleny, pokud existuje sériová posloupnost jejich jednorázového spuštění, která generuje stejný výsledek jako v tabulce. V případě operací zápisu je sériová sekvence úplně stejná jako v historii tabulky.

  • WriteSerializable (Výchozí):: Slabší úroveň izolace než Serializovatelná. Zajišťuje pouze, že operace zápisu (tj. ne čtení) jsou serializovatelné. Je to ale stále silnější než izolace snímků . WriteSerializable je výchozí úroveň izolace, protože poskytuje skvělou rovnováhu mezi konzistencí dat a dostupností pro nejběžnější operace.

    V tomto režimu se obsah tabulky Delta může lišit od toho, co se očekává od posloupnosti operací zobrazených v historii tabulek. Důvodem je to, že tento režim umožňuje určitým párům souběžných zápisů (například operací X a Y) pokračovat tak, aby výsledek byl stejný, jako kdyby byl Y proveden před X (tj. serializovatelný mezi nimi), i když historie by ukázala, že Y byl potvrzen po X. Pokud chcete toto změny pořadí zakázat, nastavte úroveň izolace tabulky tak, aby byla Serializovatelná, aby tyto transakce selhaly.

Operace čtení vždy používají izolaci snímků. Úroveň izolace zápisu určuje, zda čtenář může zobrazit snímek tabulky, že podle historie "nikdy neexistoval".

Pro serializovatelnou úroveň čtenář vždy vidí pouze tabulky, které odpovídají historii. Pro úroveň WriteSerializable může čtenář vidět tabulku, která v protokolu Delta neexistuje.

Představte si například txn1, dlouhotrvající odstranění a txn2, které vkládá data odstraněná pomocí txn1. txn2 a txn1 jsou dokončeny a jsou zaznamenány v daném pořadí v historii. Podle historie by data vložená v txn2 neměla v tabulce existovat. V případě serializovatelné úrovně by čtečka nikdy neviděla data vložená pomocí txn2. Pro úroveň WriteSerializable však čtenář může v určitém okamžiku vidět data vložená pomocí txn2.

Další informace o tom, které typy operací můžou vzájemně kolidovat v jednotlivých úrovních izolace a možné chyby, najdete v tématu Zabránění konfliktům pomocí dělení a nesouvislých podmínek příkazů.

Nastavení úrovně izolace

Úroveň izolace nastavíte pomocí ALTER TABLE příkazu.

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

kde <level-name> je Serializable nebo WriteSerializable.

Pokud chcete například změnit úroveň izolace z výchozí WriteSerializableSerializablena , spusťte:

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

Vyhněte se konfliktům pomocí podmínek dělení a oddělených příkazů

Ve všech případech označených jako "může kolidovat", zda tyto dvě operace budou kolidovat, závisí na tom, jestli pracují se stejnou sadou souborů. Tyto dvě sady souborů můžete rozdělit tak, že tabulku rozdělíte podle stejných sloupců, které se používají v podmínkách operací. Například dva příkazy UPDATE table WHERE date > '2010-01-01' ... a DELETE table WHERE date < '2010-01-01' budou v konfliktu, pokud tabulka není rozdělena podle data, protože oba se mohou pokusit upravit stejnou sadu souborů. Rozdělením tabulky date zabráníte konfliktu. Proto dělení tabulky podle podmínek běžně používaných v příkazu může významně snížit konflikty. Dělení tabulky podle sloupce s vysokou kardinalitou ale může vést k jiným problémům s výkonem kvůli velkému počtu podadresářů.

Výjimky při konfliktech

Pokud dojde ke konfliktu transakcí, zobrazí se některá z následujících výjimek:

ConcurrentAppendException

Tato výjimka nastane, když souběžná operace přidá soubory ve stejném oddílu (nebo kdekoli v tabulce bez oddílů), který vaše operace přečte. Doplňky souborů můžou být způsobeny operacemi INSERT, DELETE, UPDATE, nebo MERGE operacemi.

S výchozí úrovníWriteSerializableizolace souborů přidaných nevidomýmiINSERT operacemi (to znamená operace, které slepě připojují data bez čtení dat) nejsou v konfliktu s žádnou operací, i když se dotknou stejného oddílu (nebo kdekoli v tabulce bez dělení). Pokud je úroveň izolace nastavená na , může dojít ke Serializablekonfliktu slepých připojení.

Tato výjimka se často vyvolá během souběžných DELETEUPDATEoperací nebo MERGE operací. Zatímco souběžné operace můžou fyzicky aktualizovat různé adresáře oddílů, jeden z nich může číst stejný oddíl, který se souběžně aktualizuje, což způsobí konflikt. Tomu se můžete vyhnout tak, že oddělení v provozní podmínce explicitně uděláte. Představte si následující příklad.

// 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()

Předpokládejme, že výše uvedený kód spouštíte souběžně pro různá data nebo země. Vzhledem k tomu, že každá úloha pracuje na nezávislém oddílu v cílové tabulce Delta, neočekáváte žádné konflikty. Podmínka však není dostatečně explicitní a může prohledávat celou tabulku a může být v konfliktu se souběžnými operacemi, které aktualizují všechny ostatní oddíly. Místo toho můžete příkaz přepsat tak, aby do podmínky sloučení přidal konkrétní datum a zemi, jak je znázorněno v následujícím příkladu.

// 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()

Tato operace je teď bezpečná pro souběžné spuštění v různých datech a zemích.

ConcurrentDeleteReadException

K této výjimce dochází, když souběžná operace odstranila soubor, který operace přečetla. Mezi běžné příčiny patří DELETEoperace UPDATE, nebo MERGE operace, která přepisuje soubory.

ConcurrentDeleteDeleteException

K této výjimce dochází, když souběžná operace odstranila také soubor, který vaše operace odstraní. Příčinou můžou být dvě souběžné operace komprimace, které přepisují stejné soubory.

MetadataChangedException

K této výjimce dochází, když souběžná transakce aktualizuje metadata tabulky Delta. Běžné příčiny jsou ALTER TABLE operace nebo zápisy do tabulky Delta, které aktualizují schéma tabulky.

ConcurrentTransactionException

Pokud se dotaz streamování používající stejné umístění kontrolního bodu spustí vícekrát současně a pokusí se najednou zapisovat do tabulky Delta. Nikdy byste neměli mít dva dotazy streamování, které používají stejné umístění kontrolního bodu a spouští se současně.

ProtocolChangedException

K této výjimce může dojít v následujících případech:

  • Při upgradu tabulky Delta na novou verzi protokolu. Aby budoucí operace mohly proběhnout úspěšně, možná budete muset upgradovat databricks Runtime.
  • Když více zapisovačů vytváří nebo nahrazuje tabulku najednou.
  • Když více zapisovačů současně zapisuje do prázdné cesty.

Další podrobnosti najdete v tématu Jak Azure Databricks spravuje kompatibilitu funkcí Delta Lake?

Chování souběžnosti na úrovni řádků (starší verze)

Tato část popisuje chování verze Preview pro souběžnost na úrovni řádků v Databricks Runtime 14.1 a níže. Souběžnost na úrovni řádků vždy vyžaduje vektory odstranění.

V Databricks Runtime 13.3 LTS a novějších tabulkách s povoleným clusteringem liquid umožňují automaticky souběžnost na úrovni řádků.

V Databricks Runtime 14.0 a 14.1 můžete povolit souběžnost na úrovni řádků pro tabulky s vektory odstranění nastavením následující konfigurace pro cluster nebo SparkSession:

spark.databricks.delta.rowLevelConcurrencyPreview = true

Ve službě Databricks Runtime 14.1 a novějších výpočetních funkcích bez photonu podporuje pouze souběžnost na úrovni řádků pro DELETE operace.