it-swarm-eu.dev

Správa souběžnosti při použití vzoru SELECT-UPDATE

Řekněme, že máte následující kód (prosím ignorujte, že je to hrozné):

BEGIN TRAN;
DECLARE @id int
SELECT @id = id + 1 FROM TableA;
UPDATE TableA SET id = @id; --TableA must have only one row, apparently!
COMMIT TRAN;
-- @id is returned to the client or used somewhere else

Podle mého názoru NENÍ to správná souběžnost správně. To, že máte transakci, neznamená, že někdo jiný nebude číst stejnou hodnotu, jakou jste provedli dříve, než se dostanete k aktualizačnímu prohlášení.

Nyní ponecháme kód tak, jak je (uvědomuji si, že je to lépe zpracováno jako jediné prohlášení nebo ještě lépe pomocí sloupce autoinkrementace/identity), jaké jsou způsoby, jak zajistit, aby správně zvládal souběžnost a zabránil rasovým podmínkám, které umožňují dvěma klientům získat stejné id hodnota?

Jsem si jistý, že přidání WITH (UPDLOCK, HOLDLOCK) do SELECTu udělá trik. Zdá se, že úroveň izolace transakcí SERIALIZABLE funguje dobře, protože odepírá někomu, kdo čte, co jste udělali, dokud tranše neskončí ( [~ # ~] aktualizace [~ # ~] [ ~ # ~] : Toto je nepravdivé. Viz Martinova odpověď. Je to pravda? Budou oba pracovat stejně dobře? Je jeden preferován před druhým?

Představte si, že děláte něco legitimnějšího než aktualizace ID - nějaký výpočet založený na přečtení, které musíte aktualizovat. Mohlo by se zapojit mnoho tabulek, z nichž některé budete psát a jiné nebudete. Jaký je nejlepší postup zde?

Po napsání této otázky si myslím, že rady k uzamčení jsou lepší, protože pak uzamknete pouze tabulky, které potřebujete, ale ocenil bych něčí vstup.

P.S. A ne, nevím nejlepší odpověď a opravdu chci získat lepší porozumění! :)

25
ErikE

Jen řeší aspekt úrovně izolace SERIALIZABLE. Ano, bude to fungovat, ale s rizikem zablokování.

Dva transakce budou moci číst řádek současně. Nebudou se blokovat, protože buď vezmou objekt S lock nebo index RangeS-S zámky závislé na struktuře tabulky a tyto zámky jsou kompatibilní . Budou se však blokovat, když se pokouší získat zámky potřebné pro aktualizaci (objekt IX lock nebo index RangeS-U), což povede k zablokování.

Použití explicitního nápovědy UPDLOCK bude místo toho serializovat čtení, čímž se zabrání riziku zablokování.

11
Martin Smith

Myslím, že nejlepším přístupem pro vás by bylo, kdybyste svůj modul vystavili vysoké souběžnosti a přesvědčili se sami. Někdy stačí UPDLOCK a HOLDLOCK není třeba. Někdy funguje sp_getapplock velmi dobře. Neučinil bych zde žádné deklarace - někdy přidání dalšího indexu, triggeru nebo indexovaného pohledu změní výsledek. Musíme zdůraznit testovací kód a podívat se na sebe případ od případu.

Napsal jsem několik příkladů stresového testování zde

Úpravy: - pro lepší znalosti interních si můžete přečíst knihy Kalena Delaneye. Knihy se však mohou synchronizovat stejně jako jakákoli jiná dokumentace. Kromě toho existuje příliš mnoho kombinací, které je třeba zvážit: šest úrovní izolace, mnoho typů zámků, seskupené/nekluzové indexy a kdo ví, co jiného. To je spousta kombinací. Navíc je SQL Server uzavřeným zdrojem, takže nemůžeme stáhnout zdrojový kód, odladit ho a podobně - to by byl konečný zdroj znalostí. Po dalším vydání nebo aktualizaci Service Pack může být cokoli neúplné nebo zastaralé.

Neměli byste se tedy rozhodovat, co funguje pro váš systém, bez vlastního stresového testování. Ať už jste četli cokoli, může vám to pomoci pochopit, co se děje, ale musíte prokázat, že rady, které jste si přečetli, pro vás fungují. Nemyslím si, že to pro vás může udělat někdo.

11
A-K

V tomto konkrétním případě by přidání zámku UPDLOCK k SELECT skutečně zabránilo anomáliím. Přidání HOLDLOCK není nutné, protože blokování aktualizace je drženo po celou dobu trvání transakce, ale přiznávám se, že jsem ji sám zahrnul jako (možná špatný) zvyk v minulosti.

Představte si, že uděláte něco legitimnějšího než aktualizace ID, nějaký výpočet založený na čtení, které musíte aktualizovat. Mohlo by se zapojit mnoho tabulek, z nichž některé budete psát a jiné nebudete. Jaký je nejlepší postup zde?

Neexistují žádné osvědčené postupy. Váš výběr souběžné kontroly musí být založen na požadavcích aplikace. Některé aplikace/transakce musí být spuštěny, jako by měly výhradní vlastnictví databáze, vyhýbat se anomáliím a nepřesnostem za každou cenu. Jiné aplikace/transakce mohou tolerovat určitý stupeň vzájemného rušení.

  • Načítání úrovně pruhované zásoby (<5, 10+, 50+, 100+) pro produkt v internetovém obchodě = špinavé čtení (nepřesné nezáleží).
  • Kontrola a snižování úrovně zásob v tomto internetovém obchodě checkout = opakovatelné čtení (zásoby musíme mít, než začneme prodávat, MUSÍME skončit zápornou úrovní zásob).
  • Přesouvání hotovosti mezi mým běžným a spořicím účtem v bance = serializovatelné (nepřepočítávejte ani nemiňte své peníze!).

Úpravy: @ Komentář AlexKuznetsova vedl k tomu, že jsem znovu přečetl otázku a odstranil velmi zjevnou chybu v mé odpovědi. Poznámka pro sebe na pozdní noční účtování.

9