Share via


Isolatieniveaus en schrijfconflicten in Azure Databricks

Het isolatieniveau van een tabel definieert de mate waarin een transactie moet worden geïsoleerd van wijzigingen die door gelijktijdige bewerkingen zijn aangebracht. Schrijfconflicten in Azure Databricks zijn afhankelijk van het isolatieniveau.

Delta Lake biedt ACID-transactiegaranties tussen lees- en schrijfbewerkingen. Dit betekent dat:

  • Meerdere schrijvers in meerdere clusters kunnen tegelijkertijd een tabelpartitie wijzigen. Schrijvers zien een consistente momentopnameweergave van de tabel en schrijfbewerkingen vinden plaats in een seriële volgorde.
    • Lezers blijven een consistente momentopnameweergave zien van de tabel waarmee de Azure Databricks-taak is gestart, zelfs wanneer een tabel tijdens een taak wordt gewijzigd.

Bekijk wat zijn ACID-garanties in Azure Databricks?

Notitie

Azure Databricks maakt standaard gebruik van Delta Lake voor alle tabellen. In dit artikel wordt het gedrag voor Delta Lake in Azure Databricks beschreven.

Belangrijk

Wijzigingen in metagegevens zorgen ervoor dat alle gelijktijdige schrijfbewerkingen mislukken. Deze bewerkingen omvatten wijzigingen in het tabelprotocol, tabeleigenschappen of gegevensschema.

Streaming-leesbewerkingen mislukken wanneer ze een doorvoer tegenkomen die metagegevens van de tabel wijzigt. Als u wilt dat de stream wordt voortgezet, moet u deze opnieuw starten. Zie Productieoverwegingen voor Gestructureerd streamen voor aanbevolen methoden.

Hier volgen voorbeelden van query's die metagegevens wijzigen:

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

Schrijfconflicten met gelijktijdigheid op rijniveau

Gelijktijdigheid op rijniveau vermindert conflicten tussen gelijktijdige schrijfbewerkingen door wijzigingen op rijniveau te detecteren en automatisch conflicten op te lossen die optreden wanneer gelijktijdige schrijfbewerkingen worden bijgewerkt of verschillende rijen in hetzelfde gegevensbestand worden verwijderd.

Gelijktijdigheid op rijniveau is algemeen beschikbaar voor Databricks Runtime 14.2 en hoger. Gelijktijdigheid op rijniveau wordt standaard ondersteund voor de volgende voorwaarden:

  • Tabellen waarvoor verwijderingsvectoren zijn ingeschakeld en zonder partitionering.
  • Tabellen met vloeibare clustering, tenzij u verwijderingsvectoren hebt uitgeschakeld.

Tabellen met partities bieden geen ondersteuning voor gelijktijdigheid op rijniveau, maar kunnen nog steeds conflicten voorkomen tussen OPTIMIZE en alle andere schrijfbewerkingen wanneer verwijderingsvectoren zijn ingeschakeld. Zie Beperkingen voor gelijktijdigheid op rijniveau.

Zie voor andere Databricks Runtime-versies het gedrag van gelijktijdigheidsvoorbeelden op rijniveau (verouderd).

MERGE INTO ondersteuning voor gelijktijdigheid op rijniveau vereist Photon in Databricks Runtime 14.2. In Databricks Runtime 14.3 LTS en hoger is Photon niet vereist.

In de volgende tabel wordt beschreven welke paren schrijfbewerkingen in elk isolatieniveau kunnen conflicteren met gelijktijdigheid op rijniveau ingeschakeld.

Notitie

Tabellen met identiteitskolommen bieden geen ondersteuning voor gelijktijdige transacties. Zie Identiteitskolommen gebruiken in Delta Lake.

INSERT (1) BIJWERKEN, VERWIJDEREN, SAMENVOEGEN IN OPTIMIZE
INSERT Kan niet conflicteren
BIJWERKEN, VERWIJDEREN, SAMENVOEGEN IN Kan niet conflicteren in WriteSerializable. Kan conflicteren in Serializeerbaar bij het wijzigen van dezelfde rij. Zie Beperkingen voor gelijktijdigheid op rijniveau. Kan conflicteren bij het wijzigen van dezelfde rij. Zie Beperkingen voor gelijktijdigheid op rijniveau.
OPTIMIZE Kan niet conflicteren Kan conflicteren wanneer ZORDER BY wordt gebruikt. Kan anders niet conflicteren. Kan conflicteren wanneer ZORDER BY wordt gebruikt. Kan anders niet conflicteren.

Belangrijk

(1) Alle INSERT bewerkingen in de bovenstaande tabellen beschrijven toevoegbewerkingen die geen gegevens uit dezelfde tabel lezen voordat ze worden doorgevoerd. INSERT bewerkingen die subquery's bevatten die dezelfde tabel lezen, ondersteunen dezelfde gelijktijdigheid als MERGE.

Schrijfconflicten zonder gelijktijdigheid op rijniveau

In de volgende tabel wordt beschreven welke paren schrijfbewerkingen in elk isolatieniveau kunnen conflicteren.

Tabellen bieden geen ondersteuning voor gelijktijdigheid op rijniveau als er partities zijn gedefinieerd of als er geen verwijderingsvectoren zijn ingeschakeld. Databricks Runtime 14.2 of hoger is vereist voor gelijktijdigheid op rijniveau.

Notitie

Tabellen met identiteitskolommen bieden geen ondersteuning voor gelijktijdige transacties. Zie Identiteitskolommen gebruiken in Delta Lake.

INSERT (1) BIJWERKEN, VERWIJDEREN, SAMENVOEGEN IN OPTIMIZE
INSERT Kan niet conflicteren
BIJWERKEN, VERWIJDEREN, SAMENVOEGEN IN Kan niet conflicteren in WriteSerializable. Kan conflicteren in Serializable. Zie conflicten met partities voorkomen. Kan conflicteren in Serializable en WriteSerializable. Zie conflicten met partities voorkomen.
OPTIMIZE Kan niet conflicteren Kan niet conflicteren in tabellen waarvoor verwijderingsvectoren zijn ingeschakeld, tenzij ZORDER BY wordt gebruikt. Kan anders conflicteren. Kan niet conflicteren in tabellen waarvoor verwijderingsvectoren zijn ingeschakeld, tenzij ZORDER BY wordt gebruikt. Kan anders conflicteren.

Belangrijk

(1) Alle INSERT bewerkingen in de bovenstaande tabellen beschrijven toevoegbewerkingen die geen gegevens uit dezelfde tabel lezen voordat ze worden doorgevoerd. INSERT bewerkingen die subquery's bevatten die dezelfde tabel lezen, ondersteunen dezelfde gelijktijdigheid als MERGE.

Beperkingen voor gelijktijdigheid op rijniveau

Sommige beperkingen gelden voor gelijktijdigheid op rijniveau. Voor de volgende bewerkingen volgt conflictoplossing de normale gelijktijdigheid voor schrijfconflicten in Azure Databricks. Zie Schrijfconflicten zonder gelijktijdigheid op rijniveau.

  • Opdrachten met complexe voorwaardelijke componenten, waaronder de volgende:
    • Voorwaarden voor complexe gegevenstypen, zoals structs, matrices of kaarten.
    • Voorwaarden die niet-deterministische expressies en subquery's gebruiken.
    • Voorwaarden die gecorreleerde subquery's bevatten.
  • Voor MERGE opdrachten moet u een expliciet predicaat in de doeltabel gebruiken om rijen te filteren die overeenkomen met de brontabel. Voor samenvoegoplossing wordt het filter gebruikt om alleen rijen te scannen die conflicteren op basis van filtervoorwaarden in gelijktijdige bewerkingen.

Notitie

Conflictdetectie op rijniveau kan de totale uitvoeringstijd verhogen. In het geval van veel gelijktijdige transacties geeft de schrijver prioriteit aan latentie ten opzichte van conflictoplossing en conflicten.

Alle beperkingen voor verwijderingsvectoren gelden ook. Zie beperkingen.

Wanneer voert Delta Lake door zonder de tabel te lezen?

Delta Lake INSERT - of toevoegbewerkingen lezen de tabelstatus niet voordat ze worden doorgevoerd als aan de volgende voorwaarden wordt voldaan:

  1. Logica wordt uitgedrukt met behulp van INSERT SQL-logica of toevoegmodus.
  2. Logica bevat geen subquery's of voorwaarden die verwijzen naar de tabel waarop de schrijfbewerking is gericht.

Net als bij andere doorvoeringen valideert en lost Delta Lake de tabelversies op doorvoering met behulp van metagegevens in het transactielogboek, maar wordt er in feite geen versie van de tabel gelezen.

Notitie

Veel veelvoorkomende patronen gebruiken MERGE bewerkingen om gegevens in te voegen op basis van tabelvoorwaarden. Hoewel het mogelijk is om deze logica opnieuw te schrijven met behulp van INSERT instructies, hebben deze instructies dezelfde gelijktijdigheidsbeperkingen als MERGEwanneer een voorwaardelijke expressie verwijst naar een kolom in de doeltabel.

Serialiseerbare versus serialiseerbare isolatieniveaus schrijven

Het isolatieniveau van een tabel definieert de mate waarin een transactie moet worden geïsoleerd van wijzigingen die door gelijktijdige transacties zijn aangebracht. Delta Lake in Azure Databricks ondersteunt twee isolatieniveaus: Serializeerbaar en WriteSerializable.

  • Serialiseerbaar: het sterkste isolatieniveau. Het zorgt ervoor dat vastgelegde schrijfbewerkingen en alle leesbewerkingen serializeerbaar zijn. Bewerkingen zijn toegestaan zolang er een seriële reeks bestaat waarmee ze één voor één worden uitgevoerd, waardoor hetzelfde resultaat wordt gegenereerd als in de tabel. Voor de schrijfbewerkingen is de seriële reeks precies hetzelfde als die in de geschiedenis van de tabel.

  • WriteSerializable (standaard):een zwakker isolatieniveau dan Serializeerbaar. Het zorgt ervoor dat alleen de schrijfbewerkingen (dat wil gezegd, geen leesbewerkingen) serialiseerbaar zijn. Dit is echter nog steeds sterker dan isolatie van momentopnamen . WriteSerializable is het standaardisolatieniveau omdat het een goede balans biedt tussen gegevensconsistentie en beschikbaarheid voor de meest voorkomende bewerkingen.

    In deze modus kan de inhoud van de Delta-tabel afwijken van de inhoud die wordt verwacht van de reeks bewerkingen die in de tabelgeschiedenis worden weergegeven. Dit komt doordat deze modus bepaalde paren gelijktijdige schrijfbewerkingen (bijvoorbeeld bewerkingen X en Y) toestaat, zodat het resultaat zou zijn alsof Y vóór X werd uitgevoerd (dat wil zeggen, serialiseerbaar tussen de schrijfbewerkingen), ook al zou de geschiedenis laten zien dat Y na X is doorgevoerd. Als u deze volgorde wilt weigeren, stelt u het isolatieniveau van de tabel in op Serializeerbaar om ervoor te zorgen dat deze transacties mislukken.

Leesbewerkingen maken altijd gebruik van isolatie van momentopnamen. Het isolatieniveau voor schrijven bepaalt of het wel of niet mogelijk is voor een lezer om een momentopname van een tabel te zien, die volgens de geschiedenis nooit bestaat.

Voor het serialiseerbare niveau ziet een lezer altijd alleen tabellen die voldoen aan de geschiedenis. Voor het niveau WriteSerializable kan een lezer een tabel zien die niet in het Delta-logboek bestaat.

Denk bijvoorbeeld aan txn1, een langdurige verwijdering en txn2, waarmee gegevens worden ingevoegd die zijn verwijderd door txn1. txn2 en txn1 compleet en ze worden vastgelegd in die volgorde in de geschiedenis. Volgens de geschiedenis mogen de gegevens die in txn2 zijn ingevoegd, niet in de tabel voorkomen. Voor serializeerbaar niveau zou een lezer nooit gegevens zien die door txn2 zijn ingevoegd. Voor het writeSerializable niveau kan een lezer echter op een bepaald moment de gegevens zien die door txn2 zijn ingevoegd.

Zie Conflicten voorkomen met behulp van partitionerings- en niet-aaneengesloten opdrachtvoorwaarden voor meer informatie over welke typen bewerkingen met elkaar kunnen conflicteren op elk isolatieniveau en de mogelijke fouten.

Het isolatieniveau instellen

U stelt het isolatieniveau in met behulp van de ALTER TABLE opdracht.

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

waar <level-name> is Serializable of WriteSerializable.

Als u bijvoorbeeld het isolatieniveau wilt wijzigen van het standaardniveau WriteSerializableSerializable, voert u het volgende uit:

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

Conflicten voorkomen met behulp van partitionering en niet-aaneengesloten opdrachtvoorwaarden

In alle gevallen die zijn gemarkeerd als 'kan conflicteren', is of de twee bewerkingen conflicteren, afhankelijk van of ze op dezelfde set bestanden werken. U kunt de twee sets bestanden ontkoppelen door de tabel te partitioneren met dezelfde kolommen als de kolommen die worden gebruikt in de voorwaarden van de bewerkingen. De twee opdrachten UPDATE table WHERE date > '2010-01-01' ... en DELETE table WHERE date < '2010-01-01' zullen bijvoorbeeld conflicteren als de tabel niet is gepartitioneerd op datum, omdat beide kunnen proberen dezelfde set bestanden te wijzigen. Door de tabel te partitioneren, date voorkomt u het conflict. Daarom kan het partitioneren van een tabel volgens de voorwaarden die vaak voor de opdracht worden gebruikt, conflicten aanzienlijk verminderen. Het partitioneren van een tabel door een kolom met een hoge kardinaliteit kan echter leiden tot andere prestatieproblemen vanwege het grote aantal submappen.

Uitzonderingen bij conflicten

Wanneer er een transactieconflict optreedt, ziet u een van de volgende uitzonderingen:

ConcurrentAppendException

Deze uitzondering treedt op wanneer een gelijktijdige bewerking bestanden toevoegt in dezelfde partitie (of ergens in een niet-gepartitioneerde tabel) die uw bewerking leest. De bestands toevoegingen kunnen worden veroorzaakt doorINSERT, DELETEof UPDATEMERGE bewerkingen.

Met het standaardisolatieniveau van WriteSerializable, bestanden die zijn toegevoegd door blindeINSERT bewerkingen (dat wil gezegd, bewerkingen die blinde gegevens toevoegen zonder gegevens te lezen) conflicteren niet met een bewerking, zelfs als ze dezelfde partitie (of ergens in een niet-gepartitioneerde tabel) raken. Als het isolatieniveau is ingesteld op Serializable, kunnen blinde toevoegingen conflicteren.

Deze uitzondering wordt vaak gegenereerd tijdens gelijktijdige DELETEbewerkingen of UPDATEMERGE bewerkingen. Hoewel de gelijktijdige bewerkingen mogelijk fysiek verschillende partitiemappen bijwerken, kan een ervan dezelfde partitie lezen die de andere gelijktijdig bijwerkt, waardoor er een conflict ontstaat. U kunt dit voorkomen door de scheiding expliciet te maken in de bewerkingsvoorwaarde. Bekijk het volgende voorbeeld.

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

Stel dat u de bovenstaande code gelijktijdig uitvoert voor verschillende datums of landen. Omdat elke taak werkt op een onafhankelijke partitie in de Delta-doeltabel, verwacht u geen conflicten. De voorwaarde is echter niet expliciet genoeg en kan de hele tabel scannen en kan conflicteren met gelijktijdige bewerkingen die andere partities bijwerken. In plaats daarvan kunt u uw instructie herschrijven om een specifieke datum en land toe te voegen aan de samenvoegvoorwaarde, zoals het volgende voorbeeld laat zien.

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

Deze bewerking kan nu veilig gelijktijdig worden uitgevoerd op verschillende datums en landen.

ConcurrentDeleteReadException

Deze uitzondering treedt op wanneer een gelijktijdige bewerking een bestand heeft verwijderd dat door uw bewerking wordt gelezen. Veelvoorkomende oorzaken zijn een DELETE, UPDATEof MERGE bewerking waarmee bestanden worden herschreven.

ConcurrentDeleteDeleteException

Deze uitzondering treedt op wanneer een gelijktijdige bewerking een bestand heeft verwijderd dat uw bewerking ook verwijdert. Dit kan worden veroorzaakt doordat twee gelijktijdige compressiebewerkingen dezelfde bestanden herschrijven.

MetadataChangedException

Deze uitzondering treedt op wanneer een gelijktijdige transactie de metagegevens van een Delta-tabel bijwerken. Veelvoorkomende oorzaken zijn ALTER TABLE bewerkingen of schrijfbewerkingen naar uw Delta-tabel die het schema van de tabel bijwerken.

ConcurrentTransactionException

Als een streamingquery met dezelfde controlepuntlocatie meerdere keren tegelijk wordt gestart en tegelijkertijd naar de Delta-tabel probeert te schrijven. U mag nooit twee streaming-query's hebben die dezelfde checkpoint-locatie gebruiken en tegelijkertijd worden uitgevoerd.

ProtocolChangedException

Deze uitzondering kan zich voordoen in de volgende gevallen:

  • Wanneer uw Delta-tabel wordt bijgewerkt naar een nieuwe protocolversie. Voor toekomstige bewerkingen moet u mogelijk uw Databricks Runtime upgraden.
  • Wanneer meerdere schrijvers tegelijkertijd een tabel maken of vervangen.
  • Wanneer meerdere schrijvers tegelijkertijd naar een leeg pad schrijven.

Zie Hoe beheert Azure Databricks de compatibiliteit van Delta Lake-functies? voor meer informatie.

Preview-gedrag voor gelijktijdigheid op rijniveau (verouderd)

In deze sectie worden preview-gedrag beschreven voor gelijktijdigheid op rijniveau in Databricks Runtime 14.1 en hieronder. Gelijktijdigheid op rijniveau vereist altijd verwijderingsvectoren.

In Databricks Runtime 13.3 LTS en hoger worden in tabellen waarvoor liquide clustering is ingeschakeld, automatisch gelijktijdigheid op rijniveau ingeschakeld.

In Databricks Runtime 14.0 en 14.1 kunt u gelijktijdigheid op rijniveau inschakelen voor tabellen met verwijderingsvectoren door de volgende configuratie voor het cluster of SparkSession in te stellen:

spark.databricks.delta.rowLevelConcurrencyPreview = true

In Databricks Runtime 14.1 en lager ondersteunt niet-Photon-rekenproces alleen gelijktijdigheid op rijniveau voor DELETE bewerkingen.