Dela via


Transaktioner med minnesoptimerade tabeller

gäller för:SQL ServerAzure SQL DatabaseAzure SQL Managed Instance

Den här artikeln beskriver alla aspekter av transaktioner som är specifika för minnesoptimerade tabeller och inbyggda kompilerade lagrade procedurer.

Transaktionsisoleringsnivåerna i SQL Server gäller på olika sätt för minnesoptimerade tabeller jämfört med diskbaserade tabeller, och de underliggande mekanismerna skiljer sig åt. En förståelse av skillnaderna hjälper programmeraren att utforma ett system med högt dataflöde. Målet med transaktionsintegritet delas i alla fall.

För felvillkor som är specifika för transaktioner i minnesoptimerade tabeller går du till avsnittet Konfliktidentifiering och Logik för återförsök.

Allmän information finns i ANGE TRANSAKTIONSISOLERINGSNIVÅ (Transact-SQL).

Pessimistisk kontra optimistisk

De funktionella skillnaderna beror på pessimistiska kontra optimistiska metoder för transaktionsintegritet. Minnesoptimerade tabeller använder den optimistiska metoden:

  • Pessimistisk metod använder lås för att blockera potentiella konflikter innan de inträffar. Lås tas när uttalandet körs och frigörs när transaktionen har begåtts.

  • Optimistisk ansats identifierar konflikter när de inträffar och utför valideringskontrollerna vid commit-tidpunkten.

    • Fel 1205, ett dödläge, kan inte inträffa för en minnesoptimerad tabell.

Den optimistiska metoden är mindre omkostnader och är vanligtvis mer effektiv, delvis på grund av att transaktionskonflikter är ovanliga i de flesta program. Den största funktionella skillnaden mellan pessimistiska och optimistiska metoder är att om en konflikt inträffar, i den pessimistiska metoden väntar du, medan en av transaktionerna misslyckas och måste försökas igen av klienten i den optimistiska metoden. De funktionella skillnaderna är större när repeatable READ-isoleringsnivån är i kraft och är störst för SERIALIZABLE-nivån.

Initieringslägen för transaktioner

SQL Server har följande lägen för transaktionsinitiering:

  • Autocommit – Starten av en enkel fråga eller DML-instruktion öppnar implicit en transaktion och slutet av -instruktionen genomför implicit transaktionen. Autocommit är standardinställningen.

    • I autocommit-läget behöver man vanligtvis inte ange en tabellhint om transaktionsisoleringsnivån på den minnesoptimerade tabellen i FROM-klausul.
  • Explicit – Din Transact-SQL innehåller koden BEGIN TRANSACTION, tillsammans med en eventuell COMMIT TRANSACTION. Två eller flera uttryck kan samlas i samma transaktion.

    • I explicit läge måste du antingen använda databasalternativet MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT eller koda en tabellhint om transaktionsisoleringsnivån på den minnesoptimerade tabellen i FROM-klausulen.
  • Implicit – När SET IMPLICIT_TRANSACTION ON är i kraft. Kanske skulle ett bättre namn ha varit IMPLICIT_BEGIN_TRANSACTION, eftersom allt det här alternativet gör är implicit att utföra motsvarigheten till en explicit BEGIN TRANSACTION före varje UPDATE-instruktion om 0 = @@trancount. Därför är det upp till din T-SQL-kod att så småningom utfärda en explicit COMMIT TRANSACTION.

  • ATOMIC BLOCK – Alla instruktioner i ATOMIC-block körs alltid som en del av en enda transaktion. Antingen utförs åtgärderna i atomblocket som helhet när det lyckas eller så återställs alla åtgärder när ett fel inträffar. Varje inbyggt kompilerad lagrad procedur kräver ett ATOMIC-block.

Kodexempel med explicit läge

Följande tolkade Transact-SQL skript använder:

  • En tydlig transaktion.
  • En minnesoptimerad tabell med namnet dbo. Order_mo.
  • Kontexten READ COMMITTED transaction isolation level (READ COMMITTED-transaktionsisoleringsnivå).

Därför är det nödvändigt att ha en tabellhint för den minnesoptimerade tabellen. Tipset måste vara för ÖGONBLICKSBILD eller en ännu mer isolerande nivå. När det gäller kodexemplet är tipset WITH (SNAPSHOT). Om det här tipset tas bort skulle skriptet drabbas av ett fel 41368, för vilket ett automatiskt återförsök skulle vara olämpligt:

Fel 41368

Åtkomst till minnesoptimerade tabeller med "READ COMMITTED"-isoleringsnivån stöds endast för transaktioner med autokommitt. Det stöds inte för explicita eller implicita transaktioner. Ange en isoleringsnivå som stöds för den minnesoptimerade tabellen med hjälp av ett tabelltips, till exempel WITH (SNAPSHOT).

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;  
GO  

BEGIN TRANSACTION;  -- Explicit transaction.  

-- Order_mo  is a memory-optimized table.  
SELECT * FROM  
           dbo.Order_mo  as o  WITH (SNAPSHOT)  -- Table hint.  
      JOIN dbo.Customer  as c  on c.CustomerId = o.CustomerId;  
COMMIT TRANSACTION;

Behovet av tipset WITH (SNAPSHOT) kan undvikas med hjälp av databasalternativet MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT. När det här alternativet är inställt på ONutökas åtkomsten till en minnesoptimerad tabell under en lägre isoleringsnivå automatiskt till ögonblicksbildisolering.

ALTER DATABASE CURRENT
    SET MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT = ON;

Radversion

Minnesoptimerade tabeller använder ett mycket avancerat system för radversionshantering som gör den optimistiska metoden effektiv, även på den mest strikta isoleringsnivån i SERIALIZABLE. För mer information, se Introduktion till Memory-Optimized Tabeller.

Diskbaserade tabeller har indirekt ett system för radversionering när READ_COMMITTED_SNAPSHOT eller SNAPSHOT-isoleringsnivån är i kraft. Det här systemet baseras på tempdb, medan minnesoptimerade datastrukturer har inbyggda radversioner för maximal effektivitet.

Isoleringsnivåer

I följande tabell visas möjliga nivåer av transaktionsisolering, i följd från minsta isolering till de flesta. Mer information om konflikter som kan uppstå och omprövningslogik för att hantera dessa konflikter finns i Konfliktidentifiering och Logik för återförsök.

Isoleringsnivå Description
LÄS EJ KOMMENDERADE Inte tillgängligt: minnesoptimerade tabeller kan inte nås under Read Uncommitted-isolering. Det är fortfarande möjligt att komma åt minnesoptimerade tabeller under ögonblicksbildisolering om TRANSAKTIONSISOLERINGsnivån på sessionsnivå är inställd på READ UNCOMMITTED, med hjälp av tabelltipset WITH (SNAPSHOT) eller om du anger databasinställningen MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT till PÅ.
LÄS BEKRÄFTAD Stöds endast för minnesoptimerade tabeller när autocommit-läget är i kraft. Det går fortfarande att komma åt minnesoptimerade tabeller under ögonblicksbildisolering om transaktionsisoleringsnivån på sessionsnivå är inställd på READ COMMITTED genom att använda tabelltipset WITH (SNAPSHOT) eller ställa in databasinställningen MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT på PÅ.

Om databasalternativet READ_COMMITTED_SNAPSHOT är inställt på PÅ, är det inte tillåtet att komma åt både en minnesoptimerad och en diskbaserad tabell under READ COMMITTED-isolering i samma instruktion.
ÖGONBLICKSBILD Stöds för minnesoptimerade tabeller.

Internt är SNAPSHOT den minst krävande transaktionsisoleringsnivån för minnesoptimerade tabeller.

SNAPSHOT använder färre systemresurser än REPEATABLE READ eller SERIALIZABLE.
REPETERBAR LÄSNING Stöds för minnesoptimerade tabeller. REPEATABLE READ-isoleringen garanterar att, vid avslutning, har ingen annan samtidig transaktion kunnat uppdatera någon av de rader som lästs av den här transaktionen.

På grund av den optimistiska modellen hindras inte samtidiga transaktioner från att uppdatera rader som lästs av den här transaktionen. I stället verifierade denna transaktion vid incheckning att REPEATABLE READ-isoleringen inte har brutits. Om den har gjort det återställs den här transaktionen och måste göras om.
SERIALISERBAR Stöds för minnesoptimerade tabeller.

Med namnet Serializable eftersom isoleringen är så strikt att den nästan är som att transaktionerna körs sekventiellt snarare än samtidigt.

Transaktionsfaser och livslängd

När en minnesoptimerad tabell ingår fortsätter livslängden för en transaktion genom faserna som visas i följande bild:

hekaton_transactions

Beskrivningar av faserna följer.

Reguljär bearbetning: Fas 1 (av 3)

  • Den här fasen består av körningen av alla frågor och DML-instruktioner i frågan.
  • Under denna fas ser instruktionerna versionen av minnesoptimerade tabellerna vid den logiska starttiden för transaktionen.

Validering: Fas 2 (av 3)

  • Valideringsfasen börjar med att tilldela sluttiden, vilket markerar transaktionen som logiskt slutförd. Detta slutförande gör alla ändringar av transaktionen synliga hos andra transaktioner som är beroende av denna transaktion. De beroende transaktionerna får inte slutföras förrän den här transaktionen framgångsrikt har slutförts. Dessutom tillåts inte transaktioner som har sådana beroenden att returnera resultatuppsättningar till klienten, för att säkerställa att klienten bara ser data som har checkats in till databasen.
  • Den här fasen består av upprepningsbar läsning och serialiserbar validering. För upprepningsbar läsverifiering kontrollerar den om någon av de rader som lästs av transaktionen sedan dess har uppdaterats. För serialiserbar validering kontrollerar den om någon rad har infogats i något dataområde som genomsökts av den här transaktionen. Enligt tabellen i isoleringsnivåer och konflikter kan både upprepningsbar läs- och serialiserbar validering ske när ögonblicksbildisolering används för att verifiera konsekvensen för unika och främmande nyckelbegränsningar.

Bearbetning av åtaganden: Fas 3 (av 3)

  • Under commitfasen skrivs ändringarna av beständiga tabeller till loggen och loggen skrivs till disken. Sedan returneras kontrollen till klienten.
  • När incheckningen har slutförts meddelas alla beroende transaktioner att de kan checka in.

Som alltid bör du försöka hålla dina transaktionsenheter så minimala och korta som de är giltiga för dina databehov.

Logik för konfliktidentifiering och återförsök

Det finns två typer av transaktionsrelaterade feltillstånd som gör att en transaktion misslyckas och återställs. I de flesta fall, när ett sådant fel inträffar, måste transaktionen göras om, ungefär som när ett dödläge inträffar.

  • Konflikter mellan samtidiga transaktioner. Det här är uppdateringskonflikter och valideringsfel och kan bero på överträdelser på transaktionsisoleringsnivå eller begränsningsöverträdelser.
  • Beroendefel. Dessa problem uppstår när transaktioner som du är beroende av inte godkänns, eller när antalet beroenden blir för stort.

Följande är de felvillkor som kan orsaka att transaktioner misslyckas när de får åtkomst till minnesoptimerade tabeller.

Felkod Description Orsak
41302 Försökte uppdatera en rad som har uppdaterats i en annan transaktion sedan den aktuella transaktionen startades. Det här feltillståndet inträffar om två samtidiga transaktioner försöker uppdatera eller ta bort samma rad samtidigt. En av de två transaktionerna tar emot det här felmeddelandet och måste göras om.

41305 Upprepningsbart läsvalideringsfel. En rad som har lästs från en minnesoptimerad tabell har i denna transaktion uppdaterats av en annan transaktion som har kommitterats innan denna transaktion har begåtts. Det här felet kan inträffa när du använder REPEATABLE READ- eller SERIALIZABLE-isolering, och även om åtgärderna för en samtidig transaktion orsakar brott mot en FOREIGN KEY-begränsning.

Sådana samtidiga överträdelser av begränsningar för sekundärnyckel är sällsynta och indikerar vanligtvis ett problem med programlogik eller datainmatning. Felet kan dock också inträffa om det inte finns något index för de kolumner som ingår i villkoret FOREIGN KEY. Därför är rekommendationen att alltid skapa ett index på främmande nyckelkolumner i en minnesoptimerad tabell.

Mer detaljerade överväganden om valideringsfel som orsakas av utländska nyckelöverträdelser finns i det här blogginlägget från SQL Server Customer Advisory Team.
41325 Serialiserbart valideringsfel. En ny rad infogades i ett intervall som genomsökts tidigare av den aktuella transaktionen. Vi kallar det här en fantomrad. Det här felet kan inträffa när du använder SERIALIZABLE-isolering, och även om åtgärderna för en samtidig transaktion orsakar brott mot en PRIMÄRNYCKEL, UNIK eller FOREIGN KEY-begränsning.

Sådana samtidiga överträdelser av begränsningar är sällsynta och indikerar vanligtvis ett problem med applikationslogik eller datainmatning. På samma sätt som upprepade läsvalideringsfel kan det här felet också inträffa om det finns en FOREIGN KEY-begränsning utan index för de berörda kolumnerna.
41301 Beroendefel: ett beroende togs på en annan transaktion som senare misslyckades med att konfirmera. Den här transaktionen (Tx1) var beroende av en annan transaktion (Tx2) medan transaktionen (Tx2) var i sin validerings- eller incheckningsbearbetningsfas genom att läsa data som skrevs av Tx2. Tx2 kunde därefter inte checka in. De vanligaste orsakerna till att Tx2 inte kan checkas in är upprepningsbara läsfel (41305) och serialiserbara verifieringsfel (41325). en mindre vanlig orsak är logg-I/O-fel.
41823 och 41840 Kvoten för användardata i minnesoptimerade tabeller och tabellvariabler uppnåddes. Fel 41823 gäller för SQL Server Express/Web/Standard Edition samt enskilda databaser i Azure SQL Database. Fel 41840 gäller elastiska pooler i Azure SQL Database.

I de flesta fall indikerar dessa fel att den maximala användardatastorleken har uppnåtts, och sättet att lösa felet är att ta bort data från minnesoptimerade tabeller. Det finns dock sällsynta fall där det här felet är tillfälligt. Vi rekommenderar därför att du försöker igen när du först stöter på dessa fel.

Precis som de andra felen i den här listan gör felen 41823 och 41840 att den aktiva transaktionen avbryts.
41839 Transaktionen överskred det maximala antalet commit-beroenden. Gäller för: SQL Server 2016 (13.x). Senare versioner av SQL Server och Azure SQL Database har ingen gräns för antalet incheckningsberoenden.

Det finns en gräns för hur många transaktioner en viss transaktion (Tx1) kan vara beroende av. Dessa transaktioner är utgående beroenden. Dessutom finns det en gräns för antalet transaktioner som kan vara beroende av en viss transaktion (Tx1). Dessa transaktioner är inkommande beroenden. Gränsen för båda är 8.

Det vanligaste fallet för det här felet är att det finns ett stort antal lästransaktioner som har åtkomst till data som skrivits av en enda skrivtransaktion. Sannolikheten för att träffa det här villkoret ökar om alla lästransaktioner utför stora genomsökningar av samma data och om validerings- eller incheckningsbearbetningen av skrivtransaktionen tar lång tid, till exempel utför skrivtransaktionen stora genomsökningar under serialiserbar isolering (ökar valideringsfasens längd) eller transaktionsloggen placeras på en långsam logg-I/O-enhet (ökar längden på incheckningsbearbetningen). Om lästransaktionerna utför stora genomsökningar och de förväntas komma åt endast några rader kan ett index saknas. På samma sätt, om skrivtransaktionen använder serialiserbar isolering och utför stora genomsökningar men förväntas komma åt endast några rader, är detta också en indikation på ett index som saknas.

Gränsen för antalet commitberoenden kan lyftas med hjälp av spårningsflagga 9926. Använd endast den här spårningsflaggan om du fortfarande stöter på det här feltillståndet när du har bekräftat att det inte finns några saknade index, eftersom det kan maskera dessa problem i de ovan nämnda fallen. En annan varning är att komplexa beroendediagram, där varje transaktion har ett stort antal inkommande och utgående beroenden och enskilda transaktioner har många lager av beroenden, kan leda till ineffektivitet i systemet.

Försök med logik igen

När en transaktion misslyckas på grund av något av ovanstående villkor bör transaktionen göras om.

Logik för återförsök kan implementeras på klient- eller serversidan. Den allmänna rekommendationen är att implementera omprövningslogik på klientsidan, eftersom det är mer effektivt, och gör att du kan hantera resultatuppsättningar som returneras av transaktionen innan felet inträffar.

Exempel på T-SQL-kod igen

Logik för återförsök på serversidan med T-SQL ska endast användas för transaktioner som inte returnerar resultatuppsättningar till klienten. Annars kan återförsök potentiellt resultera i ytterligare resultatuppsättningar utöver de som förväntas returneras till klienten.

Följande tolkade T-SQL-skript illustrerar hur logik för återförsök kan se ut för fel som är associerade med transaktionskonflikter som involverar minnesoptimerade tabeller.

-- Retry logic, in Transact-SQL.
DROP PROCEDURE If Exists usp_update_salesorder_dates;
GO

CREATE PROCEDURE usp_update_salesorder_dates
AS
BEGIN
    DECLARE @retry INT = 10;

    WHILE (@retry > 0)
    BEGIN
        BEGIN TRY
            BEGIN TRANSACTION;

            UPDATE dbo.SalesOrder_mo WITH (SNAPSHOT)
                set OrderDate = GetUtcDate()
                where CustomerId = 42;

            UPDATE dbo.SalesOrder_mo WITH (SNAPSHOT)
                set OrderDate = GetUtcDate()
                where CustomerId = 43;

            COMMIT TRANSACTION;

            SET @retry = 0;  -- //Stops the loop.
        END TRY

        BEGIN CATCH
            SET @retry -= 1;

            IF (@retry > 0 AND
                ERROR_NUMBER() in (41302, 41305, 41325, 41301, 41823, 41840, 41839, 1205)
                )
            BEGIN
                IF XACT_STATE() = -1
                    ROLLBACK TRANSACTION;

                WAITFOR DELAY '00:00:00.001';
            END
            ELSE
            BEGIN
                PRINT 'Suffered an error for which Retry is inappropriate.';
                THROW;
            END
        END CATCH

    END -- //While loop
END;
GO

--  EXECUTE usp_update_salesorder_dates;

Transaktion mellan containrar

En transaktion kallas för en transaktion mellan containrar om den:

  • Får åtkomst till en minnesoptimerad tabell från tolkad Transact-SQL. eller
  • Kör en intern process när en transaktion redan är öppen (XACT_STATE() = 1).

Termen "cross-container" härleds från det faktum att transaktionen körs över de två transaktionshanteringscontainrarna, en för diskbaserade tabeller och en för minnesoptimerade tabeller.

I en enda transaktion mellan containrar kan olika isoleringsnivåer användas för åtkomst till diskbaserade och minnesoptimerade tabeller. Den här skillnaden uttrycks genom explicita tabelltips som WITH (SERIALIZABLE) eller via databasalternativet MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT, som implicit höjer isoleringsnivån för minnesoptimerad tabell till ögonblicksbild om TRANSAKTIONSISOLERINGsnivån har konfigurerats som READ COMMITTED eller READ UNCOMMITTED.

I följande Transact-SQL kodexempel:

  • Den diskbaserade tabellen, Table_D1, nås med hjälp av READ COMMITTED-isoleringsnivån.
  • Den minnesoptimerade tabellen Table_MO7 nås med hjälp av isoleringsnivån SERIALIZABLE. Table_MO6 har ingen specifik associerad isoleringsnivå, eftersom infogningar alltid är konsekventa och körs i stort sett under serialiserbar isolering.
-- Different isolation levels for
-- disk-based tables versus memory-optimized tables,
-- within one explicit transaction.

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
go

BEGIN TRANSACTION;

    -- Table_D1 is a traditional disk-based table, accessed using READ COMMITTED isolation.

    SELECT * FROM Table_D1;


    -- Table_MO6 and Table_MO7 are memory-optimized tables.
    -- Table_MO7 is accessed using SERIALIZABLE isolation,
    --   while Table_MO6 does not have a specific isolation level.

    INSERT Table_MO6
        SELECT * FROM Table_MO7 WITH (SERIALIZABLE);

COMMIT TRANSACTION;
go

Begränsningar

  • Transaktioner mellan databaser stöds inte för minnesoptimerade tabeller. Om en transaktion kommer åt en minnesoptimerad tabell kan transaktionen inte komma åt någon annan databas, förutom:

    • tempdb-databas.
    • Skrivskyddad i huvuddatabasen.
  • Distribuerade transaktioner stöds inte: När BEGIN DISTRIBUTED TRANSACTION används kan transaktionen inte komma åt en minnesoptimerad tabell.

Inhemskt kompilerade lagrade procedurer

  • I en intern process måste ATOMIC-blocket deklarera transaktionsisoleringsnivån för hela blocket, till exempel:

    • ... BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, ...) ...
  • Inga explicita transaktionskontrollinstruktioner tillåts i brödtexten i en intern process. BEGIN TRANSACTION, ROLLBACK TRANSACTION, och med flera, är alla otillåtna.

  • Mer information om transaktionskontroll med ATOMIC-block finns i Atomblock