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

Halloweenský problém – 3. část

[ Část 1 | Část 2 | Část 3 | Část 4 ]

MERGE příkaz (zavedený v SQL Server 2008) nám umožňuje provádět kombinaci INSERTs , UPDATE a DELETE operace pomocí jediného příkazu. Problémy s Halloweenskou ochranou pro MERGE jsou většinou kombinací požadavků jednotlivých operací, ale existuje několik důležitých rozdílů a několik zajímavých optimalizací, které se týkají pouze MERGE .

Jak se vyhnout halloweenskému problému s MERGE

Začneme tím, že se znovu podíváme na příklad Demo a Staging z druhé části:

CREATE TABLE dbo.Demo
(
    SomeKey integer NOT NULL,
 
    CONSTRAINT PK_Demo
        PRIMARY KEY (SomeKey)
);
 
CREATE TABLE dbo.Staging
(
    SomeKey integer NOT NULL
);
 
INSERT dbo.Staging
    (SomeKey)
VALUES
    (1234),
    (1234);
 
CREATE NONCLUSTERED INDEX c 
ON dbo.Staging (SomeKey);
 
INSERT dbo.Demo
SELECT s.SomeKey
FROM dbo.Staging AS s
WHERE NOT EXISTS
(
    SELECT 1
    FROM dbo.Demo AS d
    WHERE d.SomeKey = s.SomeKey
);

Jak si možná vzpomínáte, tento příklad byl použit k ukázce INSERTs vyžaduje halloweenskou ochranu, když je v SELECT také odkazováno na cílovou tabulku vložení část dotazu (EXISTS doložka v tomto případě). Správné chování pro INSERTs výše uvedené je pokusit se přidat obě 1234 hodnot a následně selhat s PRIMARY KEY porušení. Bez oddělení fází, INSERTs by nesprávně přidal jednu hodnotu a dokončil by se bez vyvolání chyby.

Plán provádění INSERT

Výše uvedený kód má jeden rozdíl od kódu použitého ve druhé části; byl přidán index bez klastrů do pracovní tabulky. INSERTs plán provádění stále vyžaduje však halloweenskou ochranu:

Prováděcí plán MERGE

Nyní zkuste stejnou logickou vložku vyjádřenou pomocí MERGE syntaxe:

MERGE dbo.Demo AS d
USING dbo.Staging AS s ON
    s.SomeKey = d.SomeKey
WHEN NOT MATCHED BY TARGET THEN
    INSERT (SomeKey)
    VALUES (s.SomeKey);

V případě, že nejste obeznámeni se syntaxí, logikou je porovnat řádky v tabulkách Staging a Demo na hodnotě SomeKey a pokud v cílové (Demo) tabulce není nalezen žádný odpovídající řádek, vložíme nový řádek. Toto má přesně stejnou sémantiku jako předchozí INSERT...WHERE NOT EXISTS kód, samozřejmě. Prováděcí plán je však zcela odlišný:

Všimněte si, že v tomto plánu chybí Eager Table Spool. Navzdory tomu dotaz stále vytváří správnou chybovou zprávu. Zdá se, že SQL Server našel způsob, jak provést MERGE plánujte iterativně a přitom respektujte logické oddělení fází vyžadované standardem SQL.

Optimalizace vyplňování děr

Za správných okolností může optimalizátor SQL Server rozpoznat, že MERGE příkaz je vyplňování děr , což je jen další způsob, jak říci, že příkaz přidává pouze řádky tam, kde existuje mezera v klíči cílové tabulky.

Aby se tato optimalizace použila, hodnoty použité v WHEN NOT MATCHED BY TARGET klauzule musí přesně odpovídat ON část USING doložka. Cílová tabulka musí mít také jedinečný klíč (požadavek, který splňuje PRIMARY KEY v tomto případě). Pokud jsou tyto požadavky splněny, MERGE prohlášení nevyžaduje ochranu před Halloweenským problémem.

Samozřejmě, MERGE prohlášení je logicky žádné více či méně vyplňování děr než původní INSERT...WHERE NOT EXISTS syntax. Rozdíl je v tom, že optimalizátor má úplnou kontrolu nad implementací MERGE příkaz, zatímco INSERTs syntaxe by vyžadovala, aby uvažoval o širší sémantice dotazu. Člověk může snadno vidět, že INSERTs je také zaplňování děr, ale optimalizátor nepřemýšlí o věcech stejným způsobem jako my.

Pro ilustraci přesné shody požadavek, který jsem zmínil, zvažte následující syntaxi dotazu, která není těžit z optimalizace vyplňování otvorů. Výsledkem je plná Halloweenská ochrana poskytovaná Eager Table Spool:

MERGE dbo.Demo AS d
USING dbo.Staging AS s ON
    s.SomeKey = d.SomeKey
WHEN NOT MATCHED THEN
    INSERT (SomeKey)
    VALUES (s.SomeKey * 1);

Jediný rozdíl je v násobení jednou v VALUES klauzule – něco, co nemění logiku dotazu, ale co stačí k tomu, aby se zabránilo použití optimalizace vyplňování děr.

Vyplňování otvorů pomocí vnořených smyček

V předchozím příkladu optimalizátor zvolil spojení tabulek pomocí spojení Merge. Optimalizaci vyplňování děr lze také použít tam, kde je zvoleno spojení Nested Loops, ale to vyžaduje zvláštní záruku jedinečnosti na zdrojové tabulce a hledání indexu na vnitřní straně spojení. Abychom to viděli v akci, můžeme vymazat existující pracovní data, přidat jedinečnost do neshlukovaného indexu a vyzkoušet MERGE znovu:

-- Remove existing duplicate rows
TRUNCATE TABLE dbo.Staging;
 
-- Convert index to unique
CREATE UNIQUE NONCLUSTERED INDEX c 
ON dbo.Staging (SomeKey)
WITH (DROP_EXISTING = ON);
 
-- Sample data
INSERT dbo.Staging
    (SomeKey)
VALUES
    (1234),
    (5678);
 
-- Hole-filling merge
MERGE dbo.Demo AS d
USING dbo.Staging AS s ON
    s.SomeKey = d.SomeKey
WHEN NOT MATCHED THEN
    INSERT (SomeKey)
    VALUES (s.SomeKey);

Výsledný plán provádění opět využívá optimalizaci vyplňování děr, aby se vyhnul Halloweenské ochraně, pomocí spojení vnořených smyček a vnitřního vyhledávání do cílové tabulky:

Vyhýbání se zbytečnému procházení indexu

Tam, kde se použije optimalizace vyplňování děr, může motor také použít další optimalizaci. Při čtení si dokáže zapamatovat aktuální pozici indexu cílovou tabulku (zpracovává se vždy jeden řádek, pamatujte) a znovu použijte tyto informace při provádění vkládání, namísto hledání v b-stromu k nalezení umístění vložení. Důvodem je, že aktuální pozice čtení je velmi pravděpodobně na stejné stránce, kam by měl být vložen nový řádek. Kontrola, zda řádek skutečně patří na tuto stránku, je velmi rychlá, protože zahrnuje kontrolu pouze nejnižšího a nejvyššího klíče, který je tam aktuálně uložen.

Kombinace eliminace Eager Table Spool a uložení indexové navigace na řádek může poskytnout významnou výhodu v pracovních zátěžích OLTP za předpokladu, že plán provádění je načten z mezipaměti. Náklady na kompilaci MERGE je spíše vyšší než u INSERTs , UPDATE a DELETE , takže plán opětovného použití je důležitým faktorem. Je také užitečné zajistit, aby stránky měly dostatek volného místa pro umístění nových řádků, čímž se zabrání dělení stránek. Toho je obvykle dosaženo běžnou údržbou indexu a přiřazením vhodného FILLFACTOR .

Zmiňuji úlohy OLTP, které obvykle obsahují velké množství relativně malých změn, protože MERGE optimalizace nemusí být dobrou volbou tam, kde je na jeden příkaz zpracováno velké množství řádků. Další optimalizace, jako jsou minimálně protokolované INSERTs nelze aktuálně kombinovat s výplní otvorů. Jako vždy by měly být výkonnostní charakteristiky porovnány, aby bylo zajištěno, že budou realizovány očekávané přínosy.

Optimalizace vyplňování děr pro MERGE vložky lze kombinovat s aktualizacemi a mazáními pomocí dodatečného MERGE doložky; každá operace změny dat je z hlediska Halloweenského problému posuzována samostatně.

Vyhýbání se připojení

Finální optimalizaci, na kterou se podíváme, lze použít tam, kde dojde k MERGE obsahuje operace aktualizace a odstranění, stejně jako vložení pro vyplňování děr a cílová tabulka má jedinečný seskupený index. Následující příklad ukazuje běžné MERGE vzor, ​​do kterého se vkládají neodpovídající řádky a odpovídající řádky se aktualizují nebo mažou v závislosti na další podmínce:

CREATE TABLE #T
(
    col1 integer NOT NULL,
    col2 integer NOT NULL,
 
    CONSTRAINT PK_T
        PRIMARY KEY (col1)
);
 
CREATE TABLE #S
(
    col1 integer NOT NULL,
    col2 integer NOT NULL,
 
    CONSTRAINT PK_S
        PRIMARY KEY (col1)
);
 
INSERT #T
    (col1, col2)
VALUES
    (1, 50),
    (3, 90);
 
INSERT #S
    (col1, col2)
VALUES
    (1, 40),
    (2, 80),
    (3, 90);

MERGE příkaz potřebný k provedení všech požadovaných změn je pozoruhodně kompaktní:

MERGE #T AS t
USING #S AS s ON t.col1 = s.col1
WHEN NOT MATCHED THEN INSERT VALUES (s.col1, s.col2)
WHEN MATCHED AND t.col2 - s.col2 = 0 THEN DELETE
WHEN MATCHED THEN UPDATE SET t.col2 -= s.col2;

Prováděcí plán je docela překvapivý:

Žádná Halloweenská ochrana, žádné spojení mezi zdrojovou a cílovou tabulkou a nestává se často, že uvidíte operátor vložení seskupeného indexu následovaný sloučením seskupeného indexu do stejné tabulky. Toto je další optimalizace zaměřená na pracovní zátěže OLTP s vysokým opakovaným využitím plánu a vhodnou indexací.

Cílem je přečíst řádek ze zdrojové tabulky a okamžitě se pokusit jej vložit do cíle. Pokud dojde k porušení klíče, chyba je potlačena, operátor Insert vypíše konfliktní řádek, který našel, a tento řádek je poté zpracován pro operaci aktualizace nebo odstranění pomocí operátoru plánu sloučení jako obvykle.

Pokud je původní vložení úspěšné (bez porušení klíče), zpracování pokračuje dalším řádkem ze zdroje (operátor Merge zpracovává pouze aktualizace a mazání). Tato optimalizace prospívá především MERGE dotazy, kde většina zdrojových řádků vede k vložení. Opět je nutné pečlivé srovnávání, aby byl výkon lepší než použití samostatných příkazů.

Shrnutí

MERGE poskytuje několik jedinečných možností optimalizace. Za správných okolností se může vyhnout nutnosti přidat explicitní ochranu Halloween ve srovnání s ekvivalentem INSERTs operace, nebo možná i kombinace INSERTs , UPDATE a DELETE prohlášení. Další MERGE -specifické optimalizace mohou zabránit procházení b-stromu indexu, které je obvykle potřeba k nalezení pozice vložení pro nový řádek, a mohou také zabránit nutnosti zcela spojit zdrojové a cílové tabulky.

V poslední části této série se podíváme na to, jak optimalizátor dotazů zdůvodňuje potřebu halloweenské ochrany, a identifikujeme některé další triky, které může použít, aby nebylo nutné přidávat Eager Table Spools do plánů provádění, které mění data.

[ Část 1 | Část 2 | Část 3 | Část 4 ]


  1. Neo4j - Odstranit vztah pomocí Cypher

  2. Jak automaticky znovu zadat dotaz pomocí LoaderManager

  3. Přidejte cizí klíč do existující tabulky v SQLite

  4. Jak naformátuji své dotazy Oracle tak, aby se sloupce nezalamovaly?