Isoleringsnivåer och skrivkonflikter i Azure Databricks

Isoleringsnivån i en tabell definierar i vilken grad en transaktion måste isoleras från ändringar som görs av samtidiga åtgärder. Skrivkonflikter i Azure Databricks beror på isoleringsnivån.

Delta Lake tillhandahåller ACID-transaktionsgarantier mellan läsningar och skrivningar. Det innebär att:

  • Flera skrivare i flera kluster kan samtidigt ändra en tabellpartition. Författare ser en konsekvent ögonblicksbildsvy av tabellen och skrivningar sker i serieordning.
    • Läsarna fortsätter att se en konsekvent ögonblicksbildsvy av tabellen som Azure Databricks-jobbet började med, även när en tabell ändras under ett jobb.

Se Vad är ACID-garantier på Azure Databricks?.

Kommentar

Azure Databricks använder Delta Lake för alla tabeller som standard. Den här artikeln beskriver beteendet för Delta Lake på Azure Databricks.

Viktigt!

Metadataändringar gör att alla samtidiga skrivåtgärder misslyckas. Dessa åtgärder omfattar ändringar i tabellprotokoll, tabellegenskaper eller dataschema.

Direktuppspelningsläsningar misslyckas när de påträffar en incheckning som ändrar tabellmetadata. Om du vill att strömmen ska fortsätta måste du starta om den. Rekommenderade metoder finns i Produktionsöverväganden för strukturerad direktuppspelning.

Följande är exempel på frågor som ändrar 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);

Skrivkonflikter med samtidighet på radnivå

Samtidighet på radnivå minskar konflikterna mellan samtidiga skrivåtgärder genom att identifiera ändringar på radnivå och automatiskt lösa konflikter som uppstår när samtidiga skrivningar uppdaterar eller tar bort olika rader i samma datafil.

Samtidighet på radnivå är allmänt tillgängligt på Databricks Runtime 14.2 och senare. Samtidighet på radnivå stöds som standard för följande villkor:

  • Tabeller med borttagningsvektorer aktiverade och utan partitionering.
  • Tabeller med flytande klustring, såvida du inte har inaktiverat borttagningsvektorer.

Tabeller med partitioner stöder inte samtidighet på radnivå, men kan fortfarande undvika konflikter mellan OPTIMIZE och alla andra skrivåtgärder när borttagningsvektorer är aktiverade. Se Begränsningar för samtidighet på radnivå.

Andra Databricks Runtime-versioner finns i Beteende för samtidighetsförhandsgranskning på radnivå (äldre).

I följande tabell beskrivs vilka par med skrivåtgärder som kan vara i konflikt på varje isoleringsnivå med samtidighet på radnivå aktiverat.

Kommentar

Tabeller med identitetskolumner stöder inte samtidiga transaktioner. Se Använda identitetskolumner i Delta Lake.

INSERT (1) UPPDATERA, TA BORT, SAMMANFOGA TILL OPTIMIZE
INSERT Det går inte att komma i konflikt
UPPDATERA, TA BORT, SAMMANFOGA TILL Det går inte att komma i konflikt med WriteSerializable. Kan vara i konflikt i Serializable när du ändrar samma rad. Se Begränsningar för samtidighet på radnivå. MERGE INTO kräver Photon för konfliktlösning på radnivå. Kan vara i konflikt när samma rad ändras. Se Begränsningar för samtidighet på radnivå.
OPTIMIZE Det går inte att komma i konflikt Det går inte att komma i konflikt Det går inte att komma i konflikt

Viktigt!

(1) Alla INSERT åtgärder i tabellerna ovan beskriver tilläggsåtgärder som inte läser några data från samma tabell innan de genomför. INSERT åtgärder som innehåller underfrågor som läser samma tabell stöder samma samtidighet som MERGE.

Skrivkonflikter utan samtidighet på radnivå

I följande tabell beskrivs vilka par med skrivåtgärder som kan vara i konflikt på varje isoleringsnivå.

Tabeller stöder inte samtidighet på radnivå om de har partitioner definierade eller inte har borttagningsvektorer aktiverade. Databricks Runtime 14.2 eller senare krävs för samtidighet på radnivå.

Kommentar

Tabeller med identitetskolumner stöder inte samtidiga transaktioner. Se Använda identitetskolumner i Delta Lake.

INSERT (1) UPPDATERA, TA BORT, SAMMANFOGA TILL OPTIMIZE
INSERT Det går inte att komma i konflikt
UPPDATERA, TA BORT, SAMMANFOGA TILL Det går inte att komma i konflikt med WriteSerializable. Kan vara i konflikt i Serializable; se undvika konflikter med partitioner. Kan vara i konflikt i Serializable och WriteSerializable; se undvika konflikter med partitioner.
OPTIMIZE Det går inte att komma i konflikt Det går inte att komma i konflikt med i tabeller med borttagningsvektorer aktiverade. Kan vara i konflikt med andra. Det går inte att komma i konflikt med i tabeller med borttagningsvektorer aktiverade. Kan vara i konflikt med andra.

Viktigt!

(1) Alla INSERT åtgärder i tabellerna ovan beskriver tilläggsåtgärder som inte läser några data från samma tabell innan de genomför. INSERT åtgärder som innehåller underfrågor som läser samma tabell stöder samma samtidighet som MERGE.

Begränsningar för samtidighet på radnivå

Vissa begränsningar gäller för samtidighet på radnivå. För följande åtgärder följer konfliktlösningen normal samtidighet för skrivkonflikter i Azure Databricks. Se Skriva konflikter utan samtidighet på radnivå.

  • OPTIMIZE kommandon med ZORDER BY.
  • Kommandon med komplexa villkorssatser, inklusive följande:
    • Villkor för komplexa datatyper som structs, matriser eller kartor.
    • Villkor som använder icke-deterministiska uttryck och underfrågor.
    • Villkor som innehåller korrelerade underfrågor.
  • För MERGE kommandon måste du använda ett explicit predikat i måltabellen för att filtrera rader som matchar källtabellen. För sammanslagningsmatchning används filtret endast för att genomsöka rader som kan vara i konflikt baserat på filtervillkor i samtidiga åtgärder.

Kommentar

Konfliktidentifiering på radnivå kan öka den totala körningstiden. Vid många samtidiga transaktioner prioriterar författaren svarstiden framför konfliktlösning och konflikter kan uppstå.

Alla begränsningar för borttagningsvektorer gäller också. Se Begränsningar.

När checkar Delta Lake in utan att läsa tabellen?

Delta Lake INSERT - eller tilläggsåtgärder läser inte tabelltillståndet innan du genomför om följande villkor är uppfyllda:

  1. Logik uttrycks med SQL-logik INSERT eller tilläggsläge.
  2. Logiken innehåller inga underfrågor eller villkor som refererar till den tabell som skrivåtgärden riktar in sig på.

Precis som i andra incheckningar validerar och löser Delta Lake tabellversionerna vid incheckning med hjälp av metadata i transaktionsloggen, men ingen version av tabellen läses faktiskt.

Kommentar

Många vanliga mönster använder MERGE åtgärder för att infoga data baserat på tabellvillkor. Även om det kan vara möjligt att skriva om den här logiken med hjälp av INSERT instruktioner, har dessa uttryck samma samtidighetsbegränsningar som MERGEom några villkorsuttryck refererar till en kolumn i måltabellen.

Skriv serialiserbara jämfört med serialiserbara isoleringsnivåer

Isoleringsnivån i en tabell definierar i vilken grad en transaktion måste isoleras från ändringar som görs av samtidiga transaktioner. Delta Lake på Azure Databricks stöder två isoleringsnivåer: Serializable och WriteSerializable.

  • Serialiserbar: Den starkaste isoleringsnivån. Det säkerställer att de incheckade skrivåtgärderna och alla läsningar är Serializable. Åtgärder tillåts så länge det finns en seriell sekvens där de körs en i taget som genererar samma utfall som i tabellen. För skrivåtgärderna är seriesekvensen exakt densamma som den som visas i tabellens historik.

  • WriteSerializable (standard): En svagare isoleringsnivå än Serializable. Det säkerställer bara att skrivåtgärderna (dvs. inte läsningar) är serialiserbara. Detta är dock fortfarande starkare än ögonblicksbildisolering . WriteSerializable är standardisoleringsnivån eftersom den ger bra balans mellan datakonsekvens och tillgänglighet för de vanligaste åtgärderna.

    I det här läget kan innehållet i Delta-tabellen skilja sig från det som förväntas jämfört med den åtgärdssekvens som visas i tabellhistoriken. Detta beror på att det här läget gör att vissa par samtidiga skrivningar (t.ex. åtgärder X och Y) kan fortsätta så att resultatet skulle bli som om Y utfördes före X (det vill säga serialiserbara mellan dem) även om historiken skulle visa att Y checkades in efter X. Om du inte vill tillåta den här omordningen anger du tabellisoleringsnivån till Serializable så att dessa transaktioner misslyckas.

Läsåtgärder använder alltid ögonblicksbildisolering. Skrivisoleringsnivån avgör om det är möjligt för en läsare att se en ögonblicksbild av en tabell, som enligt historiken "aldrig existerade".

För serialiserbar nivå ser en läsare alltid endast tabeller som överensstämmer med historiken. För writeSerializable-nivån kan en läsare se en tabell som inte finns i Delta-loggen.

Tänk till exempel på txn1, en tidskrävande borttagning och txn2, som infogar data som tagits bort av txn1. txn2 och txn1 slutförs och de registreras i den ordningen i historiken. Enligt historiken bör de data som infogas i txn2 inte finnas i tabellen. För serialiserbar nivå skulle en läsare aldrig se data infogade med txn2. Men för writeserializable-nivån kan en läsare någon gång se de data som infogas av txn2.

Mer information om vilka typer av åtgärder som kan vara i konflikt med varandra på varje isoleringsnivå och möjliga fel finns i Undvik konflikter med partitionering och olika kommandovillkor.

Ange isoleringsnivå

Du anger isoleringsnivån med kommandot ALTER TABLE .

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

där <level-name> är Serializable eller WriteSerializable.

Om du till exempel vill ändra isoleringsnivån från standardvärdet WriteSerializable till Serializablekör du:

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

Undvik konflikter med partitionering och osammanhängande kommandovillkor

I alla fall som har markerats som "kan vara i konflikt" beror om de två åtgärderna kommer i konflikt på om de fungerar på samma uppsättning filer. Du kan göra de två uppsättningarna filer åtskilda genom att partitionera tabellen med samma kolumner som de som används i villkoren för åtgärderna. De två kommandona UPDATE table WHERE date > '2010-01-01' ... och DELETE table WHERE date < '2010-01-01' kommer till exempel att vara i konflikt om tabellen inte är partitionerad efter datum, eftersom båda kan försöka ändra samma uppsättning filer. Om du partitionerar tabellen date efter undviker du konflikten. Därför kan partitionering av en tabell enligt de villkor som ofta används i kommandot minska konflikterna avsevärt. Att partitionera en tabell efter en kolumn med hög kardinalitet kan dock leda till andra prestandaproblem på grund av det stora antalet underkataloger.

Konfliktundantag

När en transaktionskonflikt inträffar kommer du att se något av följande undantag:

ConcurrentAppendException

Det här undantaget inträffar när en samtidig åtgärd lägger till filer i samma partition (eller var som helst i en icke-partitionerad tabell) som åtgärden läser. Filtilläggen kan orsakas av INSERT, DELETE, UPDATEeller MERGE åtgärder.

Med standardisoleringsnivån WriteSerializableför , står filer som läggs till av blindaINSERT åtgärder (dvs. åtgärder som blindt lägger till data utan att läsa några data) inte i konflikt med någon åtgärd, även om de rör samma partition (eller någonstans i en icke-partitionerad tabell). Om isoleringsnivån är inställd på Serializablekan blinda tillägg vara i konflikt.

Det här undantaget utlöses ofta under samtidiga DELETE, UPDATEeller MERGE åtgärder. De samtidiga åtgärderna kan vara att fysiskt uppdatera olika partitionskataloger, men en av dem kan läsa samma partition som den andra uppdateras samtidigt, vilket orsakar en konflikt. Du kan undvika detta genom att göra separationen explicit i åtgärdsvillkoret. Betänk följande exempel.

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

Anta att du kör koden ovan samtidigt för olika datum eller länder. Eftersom varje jobb arbetar på en oberoende partition i Delta-måltabellen förväntar du dig inga konflikter. Villkoret är dock inte tillräckligt explicit och kan genomsöka hela tabellen och kan vara i konflikt med samtidiga åtgärder som uppdaterar andra partitioner. I stället kan du skriva om instruktionen för att lägga till specifikt datum och land i sammanslagningsvillkoret, som du ser i följande exempel.

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

Den här åtgärden är nu säker att köra samtidigt på olika datum och länder.

ConcurrentDeleteReadException

Det här undantaget inträffar när en samtidig åtgärd tog bort en fil som åtgärden läste. Vanliga orsaker är en DELETEåtgärd , UPDATEeller MERGE en åtgärd som skriver om filer.

ConcurrentDeleteDeleteException

Det här undantaget inträffar när en samtidig åtgärd tog bort en fil som även åtgärden tar bort. Detta kan orsakas av två samtidiga komprimeringsåtgärder som skriver om samma filer.

MetadataChangedException

Det här undantaget inträffar när en samtidig transaktion uppdaterar metadata för en Delta-tabell. Vanliga orsaker är ALTER TABLE åtgärder eller skrivningar till deltatabellen som uppdaterar tabellens schema.

ConcurrentTransactionException

Om en direktuppspelningsfråga med samma kontrollpunktsplats startas flera gånger samtidigt och försöker skriva till Delta-tabellen samtidigt. Du bör aldrig ha två direktuppspelningsfrågor som använder samma kontrollpunktsplats och köras samtidigt.

ProtocolChangedException

Det här undantaget kan inträffa i följande fall:

  • När deltatabellen uppgraderas till en ny protokollversion. För att framtida åtgärder ska lyckas kan du behöva uppgradera Databricks Runtime.
  • När flera skrivare skapar eller ersätter en tabell samtidigt.
  • När flera skrivare skriver till en tom sökväg samtidigt.

Mer information finns i Hur hanterar Azure Databricks Delta Lake-funktionskompatibilitet?

Beteende för samtidighet på radnivå (äldre)

I det här avsnittet beskrivs förhandsgranskningsbeteenden för samtidighet på radnivå i Databricks Runtime 14.1 och nedan. Samtidighet på radnivå kräver alltid borttagningsvektorer.

I Databricks Runtime 13.3 LTS och senare aktiverar tabeller med flytande klustring automatiskt samtidighet på radnivå.

I Databricks Runtime 14.0 och 14.1 kan du aktivera samtidighet på radnivå för tabeller med borttagningsvektorer genom att ange följande konfiguration för klustret eller SparkSession:

spark.databricks.delta.rowLevelConcurrencyPreview = true

I Databricks Runtime 14.1 och nedan stöder icke-Photon-beräkning endast samtidighet på radnivå för DELETE åtgärder.