sql >> Databáze >  >> RDS >> Sqlserver

Použití transakce ROLLBACK v SQL Server

Úvod

Velmi nedávno za mnou přišel můj kolega v zoufalství a přiznal, že vydal prohlášení o aktualizaci bez klauzule WHERE na tabulce klíčových aplikací. Důsledky na frontend by byly hrozivé, a tak přišel přímo za mnou, protože naléhavě potřeboval pomoci se zvrácením situace jakýmkoli způsobem, než se začaly hrnout e-maily a eskalace.

Když jsme se na situaci podívali, zjistili jsme, že změny nebyly použity v sekundární databázi. Ve většině případů je prodleva mezi naší primární a sekundární databází dvacet minut (máme trochu zarážející, abychom se vyhnuli problémům s výkonem). Protože kolega ihned po zjištění chyby požádal o pomoc, podařilo se nám obnovit data ze sekundární databáze. Hodnotu takového zpoždění jsem popsal v tomto článku .

Přezkoumání scénáře

Scénář, který jsem popsal výše, není neobvyklý. Jedním z důvodů, proč se to stává běžným uživatelům serveru SQL Server, je to, že SQL Server používá to, co se nazývá implicitní transakce. Implicitní transakce jsou ve výchozím nastavení vypnuty, což znamená, že SQL Server neočekává, že na konci každého příkazu vydáte příkaz COMMIT TRANSACTION. Ve skutečnosti je každý příkaz potvrzen automaticky. To je pohodlné a pomáhá to předejít situacím, kdy relace, které se teprve mají provést, skončí uzamčením zdrojů a ovlivněním výkonu. Brent Ozar uvádí více podrobností o dopadech na výkon IMPLICITNÍCH TRANSAKCÍ =ON.

Nicméně malou nevýhodou této konfigurace (IMPLICITNÍ TRANSAKCE =VYPNUTO) je, že uživatelé nemají příležitost přehodnotit prohlášení a vydat ROLLBACK, což je v Oracle velmi běžné. Obr. 1 ukazuje možnosti dotazu ANSI dostupné v SQL Server Management Studio.

>Obr. 1 Výchozí ANSI v SQL Server Management Studio

Použití implicitních transakcí

V podstatě problém, se kterým se potýkáme v této výchozí konfiguraci nebo v našem nejžádanějším klientském nástroji, je ten, že po provedení příkazu SQL nelze vrátit zpět. Můžeme to obejít tím, že v naší relaci povolíme IMPLICITNÍ TRANSAKCE. To nám dá příležitost ROLLBACK transakce, pokud to budeme potřebovat. Obr. 2 a obr. 4 nám ukazují, že toto nastavení můžeme mít zapnuté pouze pro jednu relaci, i když to nezbavuje riziko, že uživatelská relace zablokuje ostatní, pokud nebude vydán ROLLBACK nebo COMMIT.

>Obr. 2 IMPLICITNÍ TRANSAKCE ZAPNUTY v jedné relaci

-- Listing 1: UPDATE Table TAB2 with IMPLICIT_TRANSACTIONS ON
SET IMPLICIT_TRANSACTIONS ON

DECLARE @IMPLICIT_TRANSACTIONS VARCHAR(3) = 'OFF';  
IF ( (2 & @@OPTIONS) = 2 ) SET @IMPLICIT_TRANSACTIONS = 'ON';  
SELECT @IMPLICIT_TRANSACTIONS AS IMPLICIT_TRANSACTIONS;

USE KTrain
GO
SELECT * FROM Tab2;
GO

UPDATE TAB2
SET countryCode='SA'
-- WHERE fname='Joyce';
GO
SELECT * FROM Tab2;
GO

Obr. 3 Všechny řádky aktualizovány

Abychom ilustrovali zde popsané zástupné řešení, podívejme se na kód SQL ve výpisu 1. Předpokládejme, že běžný uživatel SQL Server, mladší vývojář, dostal sadu skriptů, které se mají spustit za určitých podmínek . Ve skriptu byla klauzule WHERE zakomentována, protože se očekává, že při každém spuštění tohoto skriptu by měli změnit predikát. Samozřejmě se jedná o jednoduchý případ použití a riziko lze řešit mnoha způsoby, ale my jen chceme ukázat možnost provedení ROLLBACK.

Připomeňme, že jsme již zapnuli IMPLICITNÍ TRANSAKCI, takže když provedeme tento příkaz, SQL Server bude očekávat, že transakci COMMIT nebo ROLLBACK budeme. Záměrem vývojáře je aktualizovat kód země Joyce Afam do „SA“, protože emigrovala do Jižní Afriky. Obr. 3 nám ukazuje, že vývojář při pokusu o to omylem aktualizoval všechny řádky s hodnotou SA jako countryCode . Všimnou si toho a vydají ROLLBACK.

>Obr. 4 Vydání ROLLBACK

>Obr. 5 IMPLICITNÍCH TRANSAKCÍ ZAPNUTO v jiné relaci

V další relaci, ve které jsme nezapnuli IMPLICITNÍ TRANSAKCE, jsme však zjistili, že se vývojář ze své chyby nedokáže zotavit. V tomto případě nemohou úspěšně vydat ROLLBACK. Obnova by pak vyžadovala obnovu dat.

>Obr. 6 ROLLBACK není možný bez ZAPNUTÝCH IMPLICITNÍCH TRANSAKCÍ

Používání explicitních transakcí

Dalším přístupem k dosažení stejného účinku je uzavření DML do transakce výslovným uvedením BEGIN TRAN. Opět je velmi důležité dokončit transakci – buď pomocí COMMIT nebo ROLLBACK. V souvislosti s touto diskuzí vydáváme ROLLBACK, protože si uvědomujeme, že v kódu je chyba.

-- Listing 2: UPDATE Table TAB2 with Explicit Transaction

BEGIN TRAN 
GO
USE KTrain
GO
SELECT * FROM Tab2;
GO

UPDATE TAB2
SET countryCode='GH'
-- WHERE fname='Joyce';
GO
SELECT * FROM Tab2;
GO

ROLLBACK;

SELECT * FROM Tab2;
GO

- Listing 3: Corrected UPDATE Statement

BEGIN TRAN 
GO
USE KTrain
GO
SELECT * FROM Tab2;
GO

UPDATE TAB2
SET countryCode='SA'
WHERE fname='Joyce';
GO
SELECT * FROM Tab2;
GO

Závěr

V tomto článku jsme se stručně dotkli dobrého řešení pro vytváření příležitostí pro ROLLBACK a tím zmírnění chyb uživatelů vyplývajících z nesprávného DML. Zdůraznili jsme také klíčové riziko tohoto přístupu, kterým je neúmyslné zablokování. DBA může zahájit vyšetřování možné přítomnosti tohoto rizika dotazem na sys.dm_tran_session_transactions, sys.dm_tran_locks a podobné objekty dynamické správy.

Odkazy

  1. Oprava ztráty dat pomocí Log Shipping se zpožděným obnovením

  2. Nastavení implicitních transakcí

  3. Nastavit implicitní transakce jako špatný nápad

  4. DMV pro transakce


  1. Jak najít maximální hodnoty v řádcích

  2. Jak vytvořit index v Django bez výpadků

  3. Nelze se připojit k postgres ze vzdáleného hostitele

  4. Jak zjistit, zda hodnota obsahuje alespoň jedno číslo na serveru SQL