Sdílet prostřednictvím


Transakce s paměťově optimalizovanými tabulkami

platí pro: SQL Server Azure SQL DatabaseAzure SQL Managed Instance

Tento článek popisuje všechny aspekty transakcí, které jsou specifické pro tabulky optimalizované pro paměť a nativně zkompilované uložené procedury.

Úrovně izolace transakcí v SQL Serveru se liší u tabulek optimalizovaných pro paměť a tabulek založených na disku a základní mechanismy se liší. Pochopení rozdílů pomáhá programátoru navrhnout systém s vysokou propustností. Cíl integrity transakcí je sdílen ve všech případech.

V případě chybových podmínek specifických pro transakce v tabulkách optimalizovaných pro paměť přejděte do části Detekce konfliktů a logika opakování.

Obecné informace naleznete v tématu SET TRANSACTION ISOLATION LEVEL (Transact-SQL).

Pesimistické versus optimistické

Funkční rozdíly jsou způsobené pesimistickými a optimistickými přístupy k integritě transakcí. Tabulky optimalizované pro paměť používají optimistický přístup:

  • Pesimistický přístup používá zámky k blokování potenciálních konfliktů před jejich výskytem. Zámek se převezme při spuštění příkazu a uvolní při potvrzení transakce.

  • Optimistický přístup zjišťuje konflikty při jejich výskytu a provádí ověřovací kontroly v době potvrzení.

    • Chyba 1205, zablokování, nemůže nastat pro tabulku optimalizovanou pro paměť.

Optimistický přístup je méně režijní a obvykle je efektivnější, částečně proto, že ve většině aplikací jsou konflikty transakcí neobvyklé. Hlavní funkční rozdíl mezi pesimistickými a optimistickými přístupy spočívá v tom, že pokud dojde ke konfliktu, v pesimistickém přístupu čekáte, zatímco v optimistickém přístupu se jedna z transakcí nezdaří a klient ji musí opakovat. Funkční rozdíly jsou větší, když je nastavena úroveň izolace REPEATABLE READ, a jsou největší pro úroveň SERIALIZABLE.

Režimy inicializace transakcí

SQL Server má následující režimy pro inicializace transakcí:

  • Autocommit - Začátek jednoduchého dotazu nebo příkazu DML implicitně otevře transakci a konec příkazu implicitně potvrdí transakci. Funkce Autocommit je výchozí.

    • V režimu automatického dokončování obvykle není nutné zakódovat nápovědu k tabulce na úrovni izolace transakcí v tabulce optimalizované pro paměť v klauzuli FROM.
  • Explicit - Vaše Transact-SQL obsahuje kód BEGIN TRANSACTION spolu s konečnou COMMIT TRANSACTION. Dva nebo více výroků lze zahrnout do stejné transakce.

    • V explicitním režimu je nutné použít možnost databáze MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT nebo kódovat tabulku s informacemi o úrovni izolace transakcí v tabulce optimalizované pro paměť v klauzuli FROM.
  • Implicitní – když je nastavení SET IMPLICIT_TRANSACTION aktivní. Možná by byl lepší název IMPLICIT_BEGIN_TRANSACTION, protože tato možnost implicitně provádí ekvivalent explicitní BEGIN TRANSACTION před každým příkazem UPDATE, pokud 0 = @@trancount. Proto je na vašem T-SQL kódu, aby nakonec vydal explicitní COMMIT TRANSACTION.

  • ATOMIC BLOCK – Všechny příkazy v blocích ATOMIC se vždy spouští jako součást jedné transakce. Buď jsou akce atomického bloku jako celku potvrzeny při úspěchu, nebo jsou všechny akce vráceny zpět, když dojde k selhání. Každá nativně zkompilovaná uložená procedura vyžaduje blok ATOMIC.

Příklad kódu s explicitním režimem

Následující interpretovaný Transact-SQL skript používá:

  • Explicitní transakce.
  • Tabulka s názvem dbo.Order_mo optimalizovaná pro paměť.
  • Kontext úrovně izolace transakce READ COMMITTED.

Proto je nutné mít nápovědu k tabulce optimalizované pro paměť. Tip musí být pro SNAPSHOT nebo ještě více izolující úroveň. V případě příkladu kódu je tip WITH (SNAPSHOT). Pokud se tento tip odebere, skript bude mít chybu 41368, pro kterou by automatické opakování bylo nevhodné:

Chyba 41368

Přístup k tabulkám optimalizovaným pro paměť pomocí úrovně izolace READ COMMITTED je podporován pouze pro automatické transakce. Nepodporuje se pro explicitní ani implicitní transakce. Zadejte podporovanou úroveň izolace pro paměťově optimalizovanou tabulku pomocí direktivy k tabulce, například 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;

Potřebě nápovědy WITH (SNAPSHOT) lze se vyhnout použitím možnosti MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOTdatabáze. Pokud je tato možnost nastavená na hodnotu ON, přístup k tabulce optimalizované pro paměť pod nižší úrovní izolace se automaticky zvýší na úroveň izolace SNAPSHOT.

ALTER DATABASE CURRENT
    SET MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT = ON;

Verzování řádků

Tabulky optimalizované pro paměť používají vysoce sofistikovaný systém správy verzí řádků, díky kterému je optimistický přístup efektivní, a to i na nejvyšší úrovni izolace SERIALIZABLE. Podrobnosti najdete v Úvodu k tabulkám optimalizovaným pro paměť.

Diskové tabulky nepřímo mají systém verzování řádků, pokud je aktivní READ_COMMITTED_SNAPSHOT nebo úroveň izolace SNAPSHOT. Tento systém je založený na databázi tempdb, zatímco datové struktury optimalizované pro paměť mají integrovanou správu verzí řádků pro zajištění maximální efektivity.

Úrovně izolace

Následující tabulka uvádí možné úrovně izolace transakcí v pořadí od nejnižší izolace po většinu. Podrobnosti o konfliktech, ke kterým může dojít, a logiku opakování pro řešení těchto konfliktů najdete v tématu Detekce konfliktů a logika opakování.

Úroveň izolace Description
ČTENÍ NEPOTVRZENÉ Není k dispozici: K tabulkám optimalizovaným pro paměť nelze přistupovat v rámci nezaznamenaného čtení. Tabulky optimalizované pro paměť je stále možné přistupovat v rámci izolace SNAPSHOT, pokud je úroveň IZOLACE TRANSAKCE na úrovni relace nastavena na READ UNCOMMITTED, pomocí příznaku tabulky WITH (SNAPSHOT) nebo nastavením databáze MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT do stavu zapnuto.
PŘEČTENO POTVRZENO Podporováno pro tabulky optimalizované pro paměť pouze v případě, že je režim automatickéhocommitu v platnosti. Pokud je úroveň izolace transakcí na úrovni relace nastavená na READ COMMITTED, můžete k tabulkám optimalizovaným pro paměť přistupovat v rámci izolace SNAPSHOT pomocí nápovědy k tabulce WITH (SNAPSHOT) nebo nastavením databázové volby MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT na zapnuto.

Pokud je možnost databáze READ_COMMITTED_SNAPSHOT nastavená na ZAPNUTO, nemá povolený přístup k tabulce optimalizované pro paměť i k tabulce založené na disku v rámci izolace READ COMMITTED ve stejném příkazu.
SNÍMEK Podporováno pro tabulky optimalizované pro paměť.

Úroveň transakční izolace SNAPSHOT je interně nejméně náročná pro tabulky optimalizované pro paměť.

SNAPSHOT používá méně systémových prostředků, než dělá REPEATABLE READ nebo SERIALIZABLE.
OPAKOVATELNÉ ČTENÍ Podporováno pro tabulky optimalizované pro paměť. Záruka poskytovaná izolací REPEATABLE READ spočívá v tom, že v době potvrzení žádná souběžná transakce neaktualizovala žádné řádky přečtené touto transakcí.

Vzhledem k optimistickému modelu není zabráněno souběžným transakcím aktualizovat řádky přečtené touto transakcí. Místo toho tato transakce v době potvrzení ověřila, že izolace REPEATABLE READ nebyla porušena. Pokud ano, tato transakce se vrátí zpět a musí se opakovat.
SERIALIZOVATELNÝ Podporováno pro tabulky optimalizované pro paměť.

Nazvaný Serializovatelný, protože izolace je tak striktní, že je to jako mít transakce spuštěné spíše v pořadí než souběžně.

Fáze transakcí a životnost

Pokud je zahrnuta tabulka optimalizovaná pro paměť, doba života transakce prochází fázemi, jak je znázorněno na následujícím obrázku:

hekaton_transactions

Popisy fází následují.

Pravidelné zpracování: Fáze 1 (ze 3)

  • Tato fáze se skládá z provádění všech dotazů a příkazů DML v dotazu.
  • Během této fáze příkazy vidí verzi paměťově optimalizovaných tabulek v čase logického zahájení transakce.

Ověření: Fáze 2 (ze 3)

  • Fáze ověření začíná přiřazením koncového času, čímž označí transakci jako logicky dokončenou. Toto dokončení provede všechny změny transakce viditelné pro ostatní transakce, které jsou závislé na této transakci. Závislé transakce nejsou povoleny k potvrzení, dokud tato transakce nebude úspěšně potvrzena. Kromě toho transakce, které obsahují takové závislosti, nesmějí vracet sady výsledků klientovi, aby se zajistilo, že klient vidí pouze data, která byla úspěšně potvrzena do databáze.
  • Tato fáze se skládá z opakovatelného čtení a serializovatelného ověřování. K ověření opakovatelného čtení kontroluje, zda některý z řádků přečtených transakcí nebyl od té doby aktualizován. Pro serializovatelné ověření kontroluje, zda byl libovolný řádek vložen do libovolného datového rozsahu procházeného touto transakcí. Na základě tabulky v úrovních izolace a konfliktech může při použití izolace snímku dojít k opakovatelnému ověření i serializovatelnému ověřování, aby se ověřila konzistence omezení jedinečného a cizího klíče.

Zpracování potvrzení: Fáze 3 (ze 3)

  • Během fáze potvrzení se změny trvalých tabulek zapisují do protokolu a protokol se zapíše na disk. Potom se ovládací prvek vrátí klientovi.
  • Po dokončení zpracování potvrzení jsou všechny závislé transakce upozorněny, že mohou potvrdit.

Jako vždy byste se měli pokusit udržovat transakční jednotky na nezbytné minimální úrovni a co nejstručnější, jak to odpovídá vašim potřebám dat.

Detekce konfliktů a logika opakování

Existují dva druhy chybových podmínek souvisejících s transakcemi, které způsobují selhání transakce a její vrácení zpět. Ve většině případů, jakmile dojde k takové selhání, je třeba transakci opakovat, podobně jako když dojde k vzájemnému zablokování.

  • Konflikty mezi souběžnými transakcemi. Jedná se o konflikty aktualizací a selhání ověřování a může to být způsobeno porušeními úrovně izolace transakcí nebo porušením omezení.
  • Selhání závislostí. Tyto výsledky vyplývají z transakcí, které se nezdaří potvrdit, nebo z přílišného nárůstu počtu závislostí.

Níže jsou uvedené chybové podmínky, které můžou způsobit selhání transakcí při přístupu k tabulkám optimalizovaným pro paměť.

Kód chyby Description Příčina
41302 Pokusili jste se aktualizovat řádek, který byl aktualizován v jiné transakci od začátku aktuální transakce. K této chybové situaci dochází, pokud se dva souběžné transakce pokusí aktualizovat nebo odstranit stejný řádek ve stejnou dobu. Jedna z těchto dvou transakcí obdrží tuto chybovou zprávu a bude nutné ji opakovat.

41305 Selhání opakovatelného ověření čtení Řádek načtený z tabulky optimalizované pro paměť byl aktualizován jinou transakcí, která byla potvrzena před potvrzením této transakce. K této chybě může dojít při použití izolace REPEATABLE READ nebo SERIALIZABLE, a také pokud akce souběžné transakce způsobují porušení omezení CIZÍ KLÍČ.

Takové souběžné porušení omezení cizího klíče je vzácné a obvykle značí problém s logikou aplikace nebo zadáváním dat. K chybě ale může dojít také v případě, že u sloupců, které jsou součástí omezení CIZÍ KLÍČ, neexistuje žádný index. Doporučuje se vždy vytvořit index na sloupcích cizího klíče v paměťově optimalizované tabulce.

Podrobnější informace o chybách ověřování způsobených porušením cizího klíče najdete v tomto blogovém příspěvku týmu poradce pro zákazníky SQL Serveru.
41325 Selhání serializovatelného ověření Nový řádek byl vložen do rozsahu, který byl zkontrolován dříve aktuální transakcí. Říkáme tomu fantomový řádek. K této chybě může dojít při použití izolace SERIALIZABLE a také pokud akce souběžné transakce způsobují porušení omezení PRIMÁRNÍHO KLÍČE, UNIQUE nebo CIZÍHO KLÍČE.

Takové souběžné porušení omezení je vzácné a obvykle značí problém s logikou aplikace nebo zadáváním dat. Podobně jako u selhání validace opakovatelného čtení může k této chybě dojít také v případě, že existuje omezení CIZÍHO KLÍČE bez indexu u příslušných sloupců.
41301 Selhání závislosti: Byla přijata závislost na jiné transakci, která se později nepodařilo uzavřít. Tato transakce (Tx1) měla závislost na jiné transakci (Tx2), zatímco tato transakce (Tx2) byla ve fázi ověřování nebo potvrzení během zpracování, čtením dat, která byla zapsána Tx2. Tx2 se následně nepodařilo potvrdit. Nejčastějšími příčinami selhání potvrzení Tx2 jsou selhání ověření při opakovatelném čtení (41305) a serializovatelnosti (41325); méně častou příčinou je selhání vstupně-výstupní operace logu.
41823 a 41840 Došlo k dosažení kvóty pro uživatelská data v tabulkách optimalizovaných pro paměť a proměnných tabulek. Chyba 41823 platí pro SQL Server Express/Web/Standard Edition a také izolované databáze ve službě Azure SQL Database. Chyba 41840 se vztahuje na elastické fondy ve službě Azure SQL Database.

Ve většině případů tyto chyby značí, že byla dosažena maximální velikost dat uživatele a způsob, jak tuto chybu vyřešit, je odstranit data z tabulek optimalizovaných pro paměť. Existují však vzácné případy, kdy je tato chyba přechodná. Proto doporučujeme opakovat pokus při prvním výskytu těchto chyb.

Podobně jako u ostatních chyb v tomto seznamu způsobují chyby 41823 a 41840 přerušení aktivní transakce.
41839 Transakce překročila maximální počet závislostí na potvrzeních. Platí pro: SQL Server 2016 (13.x). Novější verze SQL Serveru a Azure SQL Database nemají omezení počtu závislostí na potvrzení.

Existuje limit počtu transakcí, na které může daná transakce (Tx1) záviset. Tyto transakce jsou odchozí závislosti. Kromě toho existuje limit počtu transakcí, které mohou záviset na dané transakci (Tx1). Tyto transakce jsou vstupní závislosti. Limit pro oba je 8.

Nejběžnějším případem této chyby je, že existuje velký počet transakcí čtení, které přistupují k datům zapsaným jednou transakcí zápisu. Pravděpodobnost dosažení této podmínky se zvyšuje, pokud transakce čtení provádějí velké kontroly stejných dat a pokud ověření nebo potvrzení zpracování transakce zápisu trvá dlouho, například transakce zápisu provádí velké kontroly pod serializovatelnou izolací (zvyšuje délku ověřovací fáze) nebo je transakční protokol umístěn na pomalém vstupně-výstupním zařízení protokolu (zvyšuje délku zpracování potvrzení). Pokud transakce čtení provádějí velké skeny a očekává se, že získají přístup pouze k několika řádkům, může chybět index. Podobně platí, že pokud transakce zápisu používá serializovatelnou izolaci a provádí velké skenování, ale očekává se, že bude přistupovat pouze k několika řádkům, to také naznačuje chybějící index.

Limit počtu závislostí na commitu lze zrušit pomocí příznaku trasování 9926. Jestliže se s touto chybovou podmínkou setkáváte i poté, co jste potvrdili, že nechybí žádné indexy, použijte tento příznak trasování pouze tehdy, protože v těchto případech by mohl zamaskovat uvedené problémy. Další opatrností je, že složité grafy závislostí, kde každá transakce má velký počet příchozích i odchozích závislostí a jednotlivé transakce mají mnoho vrstev závislostí, mohou vést k nekompicienci v systému.

Logika opakování

Pokud transakce selže z důvodu některé z výše uvedených podmínek, transakce by se měla opakovat.

Logiku opakování je možné implementovat na straně klienta nebo serveru. Obecně se doporučuje implementovat logiku opakování na straně klienta, protože je efektivnější a umožňuje řešit sady výsledků vrácené transakcí dříve, než dojde k selhání.

Opakování příkladu kódu T-SQL

Logika opakování na straně serveru s použitím T-SQL by se měla použít pouze pro transakce, které nevrací sady výsledků klientovi. V opačném případě může opakování potenciálně vést k dalším sadám výsledků nad rámec těch, které se očekávají, že se vrátí klientovi.

Následující interpretovaný skript T-SQL ilustruje, jak může logika opakování vypadat pro chyby spojené s konflikty transakcí zahrnující tabulky optimalizované pro paměť.

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

Transakce mezi kontejnery

Transakce se nazývá transakce mezi kontejnery, pokud:

  • Přistupuje k tabulce optimalizované pro paměť z interpretovaného jazyka Transact-SQL; nebo
  • Spustí nativní proc, pokud je transakce již otevřená (XACT_STATE() = 1).

Pojem "křížový kontejner" vychází ze skutečnosti, že transakce běží ve dvou kontejnerech správy transakcí, jeden pro tabulky založené na disku a jeden pro tabulky optimalizované pro paměť.

V rámci jedné transakce mezi kontejnery je možné použít různé úrovně izolace pro přístup k tabulkám optimalizovaným pro disky a optimalizovány pro paměť. Tento rozdíl se vyjadřuje prostřednictvím explicitních tabulkových náznaků, jako je WITH (SERIALIZABLE), nebo prostřednictvím možnosti databáze MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT, která implicitně zvýší úroveň izolace na úroveň snímku pro paměťově optimalizovanou tabulku, pokud je úroveň IZOLACE TRANSAKCE nakonfigurovaná jako READ COMMITTED nebo READ UNCOMMITTED.

V následujícím příkladu kódu Transact-SQL:

  • K tabulce založené na disku Table_D1 se přistupuje pomocí úrovně izolace READ COMMITTED.
  • K tabulce optimalizované pro paměť Table_MO7 se přistupuje pomocí úrovně izolace SERIALIZABLE. Table_MO6 nemá specifickou přidruženou úroveň izolace, protože vložení jsou vždy konzistentní a jsou v podstatě prováděna pod serializovatelnou úrovní izolace.
-- 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

Omezení

  • U tabulek optimalizovaných pro paměť se nepodporují transakce napříč databázemi. Pokud transakce přistupuje k tabulce optimalizované pro paměť, transakce nemá přístup k žádné jiné databázi s výjimkou:

    • databáze tempdb.
    • Jen pro čtení z hlavní databáze.
  • Distribuované transakce nejsou podporovány: Při použití funkce BEGIN DISTRIBUTED TRANSACTION nemůže transakce získat přístup k tabulce optimalizované pro paměť.

Nativně zkompilované uložené procedury

  • V nativním procesu musí blok ATOMIC deklarovat úroveň izolace transakce pro celý blok, například:

    • ... BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, ...) ...
  • V rámci těla nativní procedury nejsou povoleny žádné explicitní příkazy řízení transakcí. BEGIN TRANSACTION, ROLLBACK TRANSACTION a podobně jsou všechny zakázány.

  • Další informace o řízení transakcí pomocí bloků ATOMIC naleznete v tématu Atomové bloky