sql >> Databáze >  >> RDS >> Database

Oprava chyby R2 2008, která porušuje RCSI

Jedna z oprav zahrnutých v kumulativní aktualizaci 11 pro SQL Server 2008 R2 Service Pack 2 řeší „nesprávné zablokování“, ke kterému může dojít v určitém scénáři (vysvětleno dále v tomto článku). Oprava bohužel zavádí novou chybu, kdy SELECT dotazy pod RCSI (čtení izolací potvrzeného snímku) začínají přijímat zámky sdíleného záměru na úrovni tabulky. V důsledku toho můžete zaznamenat zvýšené blokování (a potenciálně uváznutí) pro dotazy RCSI po použití 2008 R2 SP2 CU11 (nebo novější).

To přijde jako nevítané překvapení pro každého, kdo je zvyklý na to, že čtenáři neblokují spisovatele (a naopak) při používání RCSI. V době psaní neexistuje žádná oprava chyby RCSI. Ve skutečnosti byla položka Connect, kterou vytvořil Eugene Karpovich k nahlášení problému, uzavřena jako „Nebude opravena“, i když chápu, že toto rozhodnutí je v současné době přezkoumáváno.

Obvykle tento problém nemusí být tak velký problém, protože kumulativní aktualizace obecně nejsou tak široce používány jako plné aktualizace Service Pack. Společnost Microsoft však nedávno oznámila, že bude k dispozici poslední aktualizace Service Pack 3 pro SQL Server 2008 R2. Tato aktualizace Service Pack bude jednoduchým souhrnem existujících kumulativních aktualizací SP2 (až do CU13 včetně), ale bez nových oprav. Výsledkem toho všeho je, že pokud se mezitím něco nezmění, uživatelé používající SP3 začnou být náhle ovlivněni chybou RCSI zavedenou v CU11.

editace:Těsně před publikováním tohoto článku společnost Microsoft potvrdila, že tato regrese bude opravena v aktualizaci SP3.

Stejná chyba „nesprávné zablokování“ (jejíž oprava zavádí novou chybu) byla také opravena v kumulativní aktualizaci 8 pro SQL Server 2012 Service Pack 1, jak je popsáno v KB2923460. Oprava pro SQL Server 2012 je jiná a není představit nový problém RCSI.

SQL Server 2014 nebyl nikdy ovlivněn žádným problémem, pokud mohu říci. Rozhodně neexistuje žádná dokumentace, která by naznačovala opak, a testy, které jsem provedl na 2014 RTM, CU1 a CU2, nereprodukují ani jednu chybu.

Chyba RCSI 2008 R2

SELECT dotaz spuštěný pod RCSI obvykle trvá pouze zámek stability schématu (Sch-S), který je kompatibilní se všemi ostatními zámky kromě zámku pro úpravu schématu (Sch-M). Když je CU11 (nebo novější) aplikována na instanci SQL Server 2008 R2, začnou tyto dotazy přijímat zámek sdíleného záměru na úrovni tabulky (Tab-IS). K demonstraci rozdílu v chování lze použít následující testovací skript:

USE master;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET NOCOUNT ON;
GO
CREATE DATABASE RCSI;
GO
ALTER DATABASE RCSI
SET READ_COMMITTED_SNAPSHOT ON;
GO
ALTER DATABASE RCSI
SET ALLOW_SNAPSHOT_ISOLATION OFF;
GO
USE RCSI;
GO
CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL, 
    col1 integer NOT NULL,
 
    CONSTRAINT PK_Test
    PRIMARY KEY CLUSTERED (id)
);
GO
INSERT dbo.Test 
    (col1) 
VALUES 
    (1), (2), (3), (4);
GO
-- Show locks
DBCC TRACEON (1200, 3604, -1) WITH NO_INFOMSGS;
SELECT * FROM dbo.Test;
DBCC TRACEOFF (1200, 3604, -1) WITH NO_INFOMSGS;
GO
ALTER DATABASE RCSI
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
 
USE master;
 
DROP DATABASE RCSI;

Při spuštění proti instanci SQL Server 2008 R2 bez chyby se ve výstupu ladění zobrazí jeden zámek Sch-S použitý pro testovací příkaz podle očekávání:

Proces získávání zámku Sch-S na OBJEKTU:7:2105058535:0 výsledek:OK
Proces uvolňující zámek na OBJEKTU:7:2105058535:0

Při spuštění proti SQL Server 2008 R2 sestavení 10.50.4302 (nebo vyšší) je výstup podobný:

Proces získávání zámku IS na OBJEKTU:7:2105058535:0 výsledek:OK
Proces uvolňuje zámek na OBJEKTU:7:2105058535:0

Všimněte si, že zámek Sch-S byl nahrazen zámkem Tab-IS.

Důsledky a zmírnění

Zámek se sdíleným záměrem (IS) je stále velmi kompatibilní zámek, ale není tak vhodný pro souběžnost jako Sch-S. Matice kompatibility zámku ukazuje, že zámek IS koliduje s:

  • Sch-M (úprava schématu) – podle Sch-S
  • BU (hromadná aktualizace)
  • X (exkluzivní)

Nekompatibilita s exkluzivními (X) zámky znamená, že čtení pod RCSI bude blokováno, pokud souběžný proces drží výhradní zámek na stejném zdroji. Stejně tak zapisovač, který potřebuje exkluzivní zámek, se zablokuje, pokud souběžná čtečka RCSI drží zámek IS. Výhradní zámky jsou získávány při každé úpravě dat a jsou drženy až do konce transakce, takže účinek chyby je ten, že čtečky pod RCSI budou blokovány souběžnými zapisovači (a naopak), když tomu tak nebylo před aplikací CU11.

Významným zmírňujícím faktorem je, že chyba způsobuje pouze úroveň tabulky záměrně sdílený zámek, který má být přijat. Souběžný zapisovač, který potřebuje úroveň tabulky exkluzivní zámek způsobí zablokování (a potenciálně uváznutí). Souběžné zapisovače, které vyžadují pouze exkluzivní zámky na nižší úrovni (např. řádek, stránka nebo oddíl), však nebudou způsobit zablokování nebo uváznutí. Na úrovni tabulky získají tyto zapisovače pouze zámek výhradní záměru (IX), který je kompatibilní s Tab-IS. Exkluzivní zámky přijaté na nižších úrovních granularity nezpůsobí konflikt.

Ve většině systémů budou výhradní zámky na úrovni tabulky (Tab-X) relativně neobvyklé. Pokud to není výslovně požadováno pomocí nápovědy TABLOCKX, některé možné příčiny uzamčení Tab-X jsou:

  • Uzamknout eskalace z nižší úrovně podrobnosti
  • Použití SERIALIZABLE bez podpůrného indexu pro zámky řady klíčů

Technickým řešením je přidat (nadbytečnou) nápovědu k tabulce WITH (READCOMMITTED) do každé tabulky v každém dotazu, který běží pod RCSI. Tím se chyba obejde, takže se použije pouze zámek Sch-S, ale to je stěží praktický návrh.

Navzdory těmto zmírněním je používání Tab-IS jako dotazu pouze pro čtení pod RCSI stále nesprávným chováním. Doufám, že to bude možné opravit pro SQL Server 2008 R2 před vydáním aktualizace Service Pack 3.

Chyba „Nesprávné zablokování“

Jak již bylo zmíněno dříve, chyba RCSI je představena jako vedlejší účinek opravy chyby „nesprávné uváznutí“. Tento dřívější problém je zdokumentován pro SQL Server 2008 R2 v KB2929464 a pro SQL Server 2012 v KB2923460. Ani jeden dokument není vzorem srozumitelnosti (nebo přesnosti), ale základní problém je docela zajímavý, takže chci strávit trochu času jeho prohlídkou zde.

K uváznutí v podstatě dojde, když:

  • Tři nebo více souběžných transakcí načtených ze stejné tabulky
  • Ve všech třech případech se používají rady UPDLOCK a TABLOCK
  • Nastavení databáze READ_COMMITTED_SNAPSHOT je ZAPNUTO

Všimněte si, že nezáleží na tom, na jaké úrovni izolace transakce probíhají. Chcete-li chybu reprodukovat, nejprve spusťte instalační skript níže:

USE master;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
CREATE DATABASE IncorrectDeadlock;
GO
ALTER DATABASE IncorrectDeadlock 
SET READ_COMMITTED_SNAPSHOT ON;
GO
USE IncorrectDeadlock;
GO
CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL, 
    col1 integer NOT NULL,
 
    CONSTRAINT PK_Test
    PRIMARY KEY CLUSTERED (id)
);
GO
INSERT dbo.Test 
    (col1) 
VALUES 
    (1);

Dále spusťte následující skript ve třech samostatných připojeních (všimněte si, že transakce zůstává otevřená):

USE IncorrectDeadlock;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
BEGIN TRANSACTION;
SELECT
    T.id,
    T.col1
FROM dbo.Test AS T
    WITH (UPDLOCK, TABLOCK);

V tomto okamžiku první relace vrátí sadu výsledků a další dvě budou zablokovány. "Nesprávné zablokování" nastane, když první relace dokončí svou transakci (buď potvrzení nebo vrácení zpět). Když k tomu dojde, jedna z dalších dvou relací ohlásí uváznutí:

K zablokování dochází, protože dvě dříve zablokované relace obsahují Tab-IX (exkluzivní záměr na úrovni tabulky) a obě chtějí převést svůj zámek na Tab-X (exkluzivní na úrovni tabulky). Tab-IX je kompatibilní s jiným Tab-IX, ale ne Tab-X. Toto je zablokování převodu (a ironií je, že UPDLOCK se často používá, aby se zabránilo zablokování převodu).

Neváhejte změnit úroveň izolace transakcí pro tři dotazy, jak chcete. Zablokování nastane bez ohledu na to, pokud je povoleno RCSI, se stejnými zámky. Po dokončení testů odeberte testovací databázi:

USE IncorrectDeadlock;
 
ALTER DATABASE IncorrectDeadlock
SET SINGLE_USER 
WITH ROLLBACK IMMEDIATE;
 
USE master;
 
DROP DATABASE IncorrectDeadlock;

Analýza a vysvětlení

Osobně si nevzpomínám, že bych někdy ve svém kódu použil UPDLOCK a TABLOCK společně. Tato kombinace rad mi připadá intuitivně zvláštní, protože SQL Server nemá zámek aktualizace na úrovni tabulky . Takže, co to vůbec znamená specifikovat společně tipy UPDLOCK a TABLOCK?

Dokumentace uvádí toto:

UPDLOCK

Určuje, že aktualizační zámky mají být převzaty a drženy, dokud se transakce nedokončí. UPDLOCK přebírá aktualizační zámky pro operace čtení pouze na úrovni řádku nebo stránky. Pokud je UPDLOCK kombinován s TABLOCK nebo je z nějakého jiného důvodu použit zámek na úrovni tabulky, použije se místo něj exkluzivní zámek (X).

To naznačuje, že kombinace nápovědy by měla vést k jedinému exkluzivnímu uzamčení tabulky. Ve skutečnosti to není tak úplně celý příběh:

V SQL Server 2000 výsledkem kombinace UPDLOCK a TABLOCK tipů je převzat Tab-S (zámek sdílené tabulky) následovaný převodem na Tab-X (exkluzivní zámek tabulky) pod všemi úrovněmi izolace kromě READ UNCOMMITTED. Tato sekvence zámků může vést k uváznutí, kdy se jedná o tři nebo více relací:dvě relace získají Tab-S a obě čekají na druhou, než se převedou na Tab-X. V části READ UNCOMMITTED SQL Server 2000 přebírá Sch-S a poté Tab-X, což není náchylné k uváznutí (jen normální blokování).

V SQL Server 2005 a novějších (bez opravy chyby) závisí přijaté zámky pouze zda je RCSI povoleno nebo ne. Pokud je povoleno RCSI, všechny úrovně izolace vezměte Tab-IX a poté převeďte na Tab-X. Tato sekvence způsobí zablokování adres pro opravu chyb.

Pokud není povoleno RCSI, chovají se odpovídající úrovně izolace stejně jako v SQL Server 2000 (převzít Tab-S a poté převést na Tab-X). Úroveň izolace snímku (nová pro rok 2005) má Sch-S následovaný Tab-X. V důsledku toho jsou SI a READ UNCOMMITTED jediné úrovně izolace, které nejsou náchylné k tomuto uváznutí ve scénáři UPDLOCK, TABLOCK, když není povoleno RCSI.

Oprava zablokování

Oprava změní zámky přijaté, když jsou společně zadány UPDLOCK a TABLOCK, pro všechny úrovně izolace a bez ohledu na to zda je RCSI povoleno nebo ne. Po použití opravy UPDLOCK a TABLOCK způsobí, že engine získá Tab-SIX (úroveň tabulky sdílené s výhradním záměrem), který je poté převeden na Tab-X.

Tím se zabrání zablokování, protože Tab-SIX je nekompatibilní s jiným Tab-SIX. Pamatujte, že k uváznutí došlo, když dva procesy udržely Tab-IX čekající na převod na Tab-X. Když je Tab-IX nahrazen Tab-SIX, není možné, aby oba držely Tab-SIX současně. Výsledkem je normální scénář blokování namísto uváznutí.

Poslední myšlenky

Oprava „nesprávného zablokování“ sice řeší jeden konkrétní scénář zablokování, ale stále nevede k chování, jaké si představuji, že lidé specifikující UPDLOCK a TABLOCK předpokládali. Pokud by SQL Server měl zámek Tab-U (aktualizace na úrovni tabulky), zabránil by souběžným změnám v tabulce, ale umožnil by souběžné čtečky. To je to, co si představuji, že by byl záměr lidí, kteří by tyto rady používali společně, a vidím, jak by to mohlo být užitečné.

Současná implementace (kde se nakonec použije Tab-X místo chybějícího Tab-U) tomuto očekávání neodpovídá, protože Tab-X zabraňuje souběžnému čtení (pokud není použita úroveň izolace verzování řádků). V mnoha případech bychom také mohli specifikovat TABLOCKX. Skutečnost, že oprava také přináší novou chybu (pouze pro uživatele SQL Server 2008 R2), je také nešťastná, zvláště pokud bude chyba zahrnuta do 2008 R2 SP3.

Všimněte si, že oprava zablokování není k dispozici pro verze SQL Server starší než 2008 R2. Tyto verze budou mít i nadále složité zamykání pro UPDLOCK a TABLOCK, jak je popsáno výše.

Děkuji Eugene Karpovichovi, který mě jako první upozornil na tento problém v komentáři k mému článku o úpravách dat podle RCSI.


  1. Úlohy hybridní databáze OLTP/Analytics:Replikace dat MySQL do ClickHouse

  2. Čekání na localhost, navždy!

  3. Nejlepší způsob, jak vybrat náhodné řádky PostgreSQL

  4. Jak mohu procházet všechny řádky tabulky? (MySQL)