Snapshot elszigetelési szint a SQL Server 2005-től

Eléggé elfeledett téma ez az SQL Serverben, pedig önmagában ez az új szolgáltatás eladta volna az SQL 2005-öt.
Update: korábban itt egy url volt a technetre, de onnan lekerült a cikk, ezért feltöltöttem ide:

Bevezető az elszigetelési szintekbe
Az SQL Servernek biztosítani kell a párhuzamosan futó adatmódosító és olvasó folyamatok békés egymás mellett élését. A párhuzamos műveletek egymásra hatását teljesen meg lehetne akadályozni, ám ez súlyosan visszavetné az adatbázis kiszolgálási sebességét. Ehhez egyszerűen csak be kellene rakni egy kapuőrt, aki addig nem enged be egy új tranzakciót, míg az éppen futó konklúzióra nem jutott, azaz committal vagy rollback-kel véget nem ért. Könnyen belátható, hogy ezzel kiirtanánk minden párhuzamosságot az adatbáziskezelőből, egyfelhasználósra degradálnánk.
A káosz és a sorosított tranzakciók között vannak kompromisszumos közbenső szintek, ezek az elszigetelési szintek, isolation level-ök. Könnyű elképzelni, hogy két író műveletet, amelyek ugyanarra az erőforrásra, pl. tábla sorra vonatkoznak, nem lehet párhuzamosítani. Így az elszigetelési szintek a párhuzamos író és olvasó folyamatok közötti áthatást tudják szabályozni. Egészen pontosan, az elszigetelési szintek a selectekre hatnak. Az insert, update, delete-re nem, ezt érdemes a fejünkbe vésni már az elején.

A kérdések a következők:
1. Láthat-e egy select olyan adatokat, amelyeket egy másik, nyitott tranzakció éppen módosít (dirty records)?
2. Elvárható-e, hogy egy tranzakcióban a kiolvasott adatokat ugyanazon selecttel újra kiolvasva pontosan ugyanúgy néznek ki a sorok, mint korábban, vagy megváltozhatnak a két olvasás között, azaz, megismételhető-e az olvasás (repeatable read)?
3. Mi van, ha az előbbi olvasások között új sorokat szúrnak be a táblába? Láthatóvá válnak-e ezek a szellem sorok a második olvasásra (phantom records)?

Az SQL Server mindhárom kérdésre ad válaszokat, már idétlen idők óta.

A READ UNCOMMITTED elszigetelési szinten futó select láthatja az éppen módosítás alatt álló sorokat, amelyeket lehet, hogy egy másodperc múlva már vissza is görgetnek.
A READ COMMITTED alapértelmezett szinten nem látunk piszkos rekordokat, de belefuthatunk a nem megismételhető olvasás és a fantom sorok problémájába.
A REPEATABLE READ szinten garantálják az olvasások megismételhetőségét, de fantom sorok még így is megjelenhetnek.
A SERIALIZABLE szint az összes olvasási anomáliára gyógyír, cserébe a párhuzamosság nagyon erősen lecsökken.
Ezen szinteket a zárolások idejének és módjának variálásával képes előállítani a szerver, a részletekről egy régebbi cikkemből érdemes tájékozódni.
http://soci.hu/publications.aspx
Jól látható, hogy az elszigetelési szintek megfelelő megoldást adnak a különböző élethelyzetekben fellépő párhuzamossági problémákra. Vagy mégsem? Ha így lenne, nem jött volna létre ez a cikk.

A Snapshot elszigetelési szint alapjai
Az előző szintek közös ideológiai és technológiai jellemzője, hogy zárolássokkal védik meg egymástól a párhuzamos folyamatokat. Ez elvileg korrekt megoldást ad, de időnként túlságosan lekorlátozza a szerver párhuzamosságát.
Jön egy folyamat, indít egy tranzakciót, amiben nekiáll módosítani egy tábla felét. A módosítás mondjuk 5 percig tart. Ez idő alatt READ COMMITTED vagy szigorúbb szinten senki nem tudja olvasni a tábla módosítás alatt álló részeit, mert zárolás alatt állnak. Miért nem lehet azt tenni, hogy a select visszakapja a sorok módosítás előtti állapotát?
Hasonlóan, ha fut egy nagyobb analitikai lekérdezés vagy riport, ami intenzíven olvassa a tábla tartalmát, akkor miért nem engedik módosítani közben a sorokat, úgy, hogy a select az adatok módosítás előtti állapotát lássa? Miért ne? Erre találták ki a snapshot elszigetelési szintet.
Snapshot esetén nem zárolássokkal dolgozik a szerver, hanem ún. sorverziózással. Amikor egy SNAPSHOT izolációs szinten futó folyamat adatokat kérdez le, nem rak a szerver csak olvasható (shared) zárakat a sorokra. Ehelyett, ha jön író folyamat, a módosított sorok módosítás előtt állapotát letárolja a tempdb-ben, az ún. version store-ban, verziótárban. Ha az olvasó folyamat vagy akár más folyamatok is újra a kérdéses adatokat select-álják vissza, a verziótárból szedi össze a szerver a módosított sorok korábbi állapotát. Ha többen is nekilátják módosítani ugyanazt a tartományt, akkor többféle verzió is keletkezik a tempdb-ben, amelyek láncolt listában tárolódnak, és lekérdezéskor ebből keresi ki a szerver a szükséges verziót.
Mit nyerünk és mit vesztünk, hogy áll a zárolás kontra sorverziózás vetélkedő? A verziózás nyilvánvaló (óriási) előnye, hogy az olvasók nem blokkolják az írókat és vica versa. Végül is ezért vezették be a szintet. Sajnos viszont az is nyilvánvaló, hogy a módosítások lassabbak lesznek, mivel verziózni kell minden módosított sort. A lekérdezések is lassabbak lesznek, mert ha vannak nyitott író tranzakciók, utána kell nézni a soroknak a tempdb-ben is, hátha van korábbi verziójuk.
Másfelől viszont verziózás esetén nem kell shared lockokat rakni az olvasott sorokra vagy lapokra, így ez a költsége meg kiesik. A kettő közötti döntéshez pontosabban meg kell ismernünk a snapshot elszigetelés belső működést, vágjunk hát bele a részletekbe.
Snapshot üzemmódok bekapcsolása
Kétféle módon használhatjuk a snapshot elszigetelési szintet, utasítás és tranzakció szinten is, fontos megérteni a pontos különbséget a kettő között.
Egyik snapshot üzemmód sincs alapban bekapcsolva az adatbázisokon, azaz az SQL 2005-ös és 2008-as adatbázisok is zárolással működnek, hasonlóan az SQL Server 2000-hez. Bekapcsolásuk ALTER DATABASE-zel lehetséges:

alter database SnapDB
set allow_snapshot_isolation on

illetve

alter database SnapDB
set read_committed_snapshot on

Bekapcsolás után erősen megnőhet a tempdb mérete és terhelése, erről hamarosan részletesen írok. Minden egyes tábla sora, amelyen update vagy delete műveletet futtatunk, kap egy 14 bájtos plusz adattagot, egy mutatót, azaz ennyivel lesz hosszabb minden módosított sor. Ettől laptörések (page split) következhetnek be, ami töredezetté teheti a táblát, így bekapcsolás és némi használat után érdemes megfigyelni az érintett táblák töredezettségi szintjét, de defragmentálni, ha indokolt.

Snapshot Isolation (SI)
Ha az allow_snapshot_isolation be van kapcsolva, akkor a tranzakciókban átválthatunk SNAPSHOT elszigetelési szintre, amitől tranzakció-szintű sorverziózást kapunk.
Alapállás:

create table Gyumolcs (id int, nev nvarchar(50))
insert Gyumolcs values(1, N'Alma')

“A” kapcsolaton végrehajtjuk a következőt:

begin tran
update Gyumolcs set nev = N'Körte' where id = 1	

Látható, a tranzakció nyitva maradt. “B” kapcsolaton:

set transaction isolation level snapshot
begin tran
select * from Gyumolcs
-- 1 Alma

Átváltottunk snapshot elszigetelési szintre, és felolvassuk a táblát. Az első megfigyelés, hogy nem blokkolódik az olvasó (B) tranzakció, hanem azonnal visszatér a sor módosítás előtti állapotával, azaz az Almával. Snapshot nélkül a select várakozna az első tranzakció végére, commit estén a módosítás utáni állapotot látnánk, rollbacknél az előttit.
Zárjuk le az első tranzakciót, véglegesítve a módosítást:

Commit	

Adjuk ki még egyszer a selectet a második, még nyitott tranzakcióban:

select * from Gyumolcs
-- 1 Alma

Kimenet: továbbra is Alma. Miért? SNAPSHOT szinten tranzakció-szintű sorverziózást kapunk, azaz garantáltan ugyanazt az adatok kapjuk vissza második olvasásra is, amit a tranzakció elején. REPEATABLE READ, sorverziózással.
Zárjuk le a második tranzakciót is egy committal. Ekkor nem történik adatbázisműveletet, hisz csak olvastunk ebben a tranzakcióban, de a verziótár tudja, hogy lehet takarítani a korábban eltárolt sort, nincs már senkinek szüksége rá. Ebből következik, hogy hosszú ideig nyitott tranzakciók miatt igen nagyra tud nőni a verziótár (a tempdb), így erre fokozottan kell ügyelni az adatelérő réteg tervezésénél.
Tovább vizsgálódva nézzük meg, mi a helyzet a fantomsorokkal?
A másodikon kapcsolaton:

set transaction isolation level snapshot
begin tran
select * from Gyumolcs
-- 1 Körte

Kimenet: Körte, az előző teszt módosításának végeredménye.

Az első kapcsolaton szúrjunk be egy új sort:

begin tran
insert Gyumolcs values(2, N'Barack')	
	select * from Gyumolcs
-- 1 Körte

A második kapcsolaton megismételve a selectet továbbra is csak a Körte sor látszik, az új Barack nem. Ez akkor is így marad, ha commitoljuk az első tranzakció beszúrását. Azaz látjuk, hogy a SNAPSHOT elszigetelési szint véd a fantom sorok ellen is. Ez azért nagy szám, mert ugyanazokat a tranzakcionális garanciákat kapjuk SNAPSHOT szinten, mint a zárolós működési módban a legszigorúbb SERIALIZABLE szinten, mégis, a folyamatok párhuzamosságát sokkal kevésbé korlátozva. Ennek nyilván a verziótár állandó frissítésével fizetjük meg az árát.
Rendben, a módosítások és a frissítések jól megférnek egymás mellett, és garantálja nekünk a szerver, hogy a lekérdezések az őket magába foglaló tranzakció kezdetéhez képesti legfrissebb adatot adja nekünk vissza. Ez adatelemzési szempontból óriási előny lehet, azonban a módosításokat kicsit megnehezíti. Miért?
Hajtsuk végre a következő scriptet az B kapcsolaton keresztül:

set transaction isolation level snapshot
begin tran
select * from Gyumolcs 
where id = 1
-- 1 Körte

Látjuk a Körte sort, eddig semmi meglepő.
A kapcsolaton módosítjuk B kapcsolaton leválogatott sort:

begin tran
update Gyumolcs set nev = N'Narancs' where id = 1	

A Körte most már Narancs, de még nincs véglegesítve a tranzakció. B folyamat újraolvashatná a sort, és látná az eredeti állapotot, az Körtét, ezt már tudjuk. De B most módosítani akarja ugyanazt a sort!

update Gyumolcs 
set nev = N'Narancs' where id = 1
-- Várakozik ...

Mit tehet ilyenkor a szerver? Olvasás esetén elővehette az eredeti verziót, de ha most is ezt tenné, akkor a tranzakció végén fejbe vágná a két tranzakció egymás módosítását, azaz a sor végső állapotát az döntené el, hogy melyik tranzakció végzett később. Ezt a problémát lost update-nek nevezzük, elveszett módosításnak. A cikk elején felsorol anomáliák között azért nem szerepelt ez, mert ez csak optimista, nem zárolós sémák esetén jelentkezik, a zárolós-pesszimista üzemmódnál nem.
No, az SQL Server úgy kezeli ezt az esetet, hogy megvárakoztatja a második módosító tranzakciót. Ha az első egy committal ezek után eldönti, mit akar, a második megkapja a magáét:

commit	

Msg 3960, Level 16, State 4, Line 1
Snapshot isolation transaction aborted due to update conflict. You cannot use snapshot isolation to access table ‘dbo.Gyumolcs’ directly or indirectly in database ‘SnapDB’ to update, delete, or insert the row that has been modified or deleted by another transaction. Retry the transaction or change the isolation level for the update/delete statement.

Logikus lépés, valahogyan tudatni kell a második tranzakcióval, hogy megelőzték. A tranzakciót már le se kell zárni, mert ezt megtette helyettünk a szerver. Sőt, nem is szabad, hisz nincs értelme, ezt is gyorsan megtapasztaljuk:

	commit

Msg 3902, Level 16, State 1, Line 1
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

Azaz az első tranzakció sikeresen módosított a sort. Mit tehet a vesztes tranzakció? Ott van a tipp a hibaüzenetben, újra lehet próbálkozni. Másodikra talán mi leszünk a gyorsabbak. Ha belegondolunk, a zárolós-pesszimista üzemmód esetén is vannak hasonló esetek, ott deadlockok esetén kell ismétlő logikát berakni az alkalmazásba, itt pedig a 3960-as hibát kell speciálisan lekezelni (az adatelérő rétegben, vagy egy TSQL TRY-CATCH-ben).
Ez a hiba valamelyest segít dönteni abban, hogy érdemes-e átváltanunk erre az új elszigetelési szintre. Ha kevés módosítási konfliktust élünk meg, azaz sok az olvasó folyamat, de kevés az módosító, akkor a SNAPSHOT jó választás, nagyon sokat segíthet a párhuzamos végrehajtásban, így a szerver áteresztő képességében (hány tranzakciót tud végrehajtani egységnyi idő alatt).
Viszont sok párhuzamos író folyamat esetén (pl. sok forrásból táplálkozó adatgyűjtő rendszer) a régi zárolós üzemmód a megfelelőbb. Ebben az esetben viszont a deadlockokat kell ismétléssel lekezelni, csak ezekből várhatóan kevesebb lesz, így erre az oldalra billen el a mérleg nyelve.

Read Committed Snapshot Isolation (RCSI)

RCSI esetén nem kell explicit átváltani SNAPSHOT szintre set transaction isolation level paranccsal, hanem az eddigi alapértelmezett READ COMMITTED szint működése alakul át verziózottá. Azaz anélkül, hogy a kisujjunkat is meg kellene mozgatni, máris élvezhetjük a megnövekedett párhuzamosságot (meg a megnövekedett tempdb-t :).
A SI és az RCSI között azonban alapvető különbség van: míg SI estén láttuk, hogy az olvasó tranzakció kezdetéhez képest kapunk vissza konzisztens adatokat, ismételt olvasási garanciával, addig RCSI esetén a select utasítás kezdetéhez képest kapunk vissza egységes adatokat. Azaz nincs ismételt olvasási és fantom sorok elleni garancia.
Nézzük meg az első példánkat, ezúttal RCSI szinten. Hogy teljesen tiszta legyen a kép, kapcsoljuk ki a sima SI-t:

alter database SnapDB
set allow_snapshot_isolation off

Majd váltsunk át az RCSI üzemmódba a következő paranccsal:

alter database SnapDB
set read_committed_snapshot on

A parancs csak akkor hajtódik végre, ha csakis ezt a parancsot végrehajtó kapcsolat van benn az adatbázisban. Ügyeljünk, hogy a Management Studio Object Browsere is nyitva tart egy kapcsolatot az adatbázisra, ha annak belseje ki van nyitva a fában. Zárjuk be a fa csomópontját, és felszabadul a kapcsolat.
Bekapcsolás után hajtsuk végre a korábbi tesztünket ezen a szinten:

Conn1:

1.
create table Gyumolcs (id int, nev nvarchar(50))
insert Gyumolcs values(1, N'Alma')	

Conn2:

2.
begin tran
select * from Gyumolcs --1 Alma

Conn1:

3.
begin tran
update Gyumolcs set nev = N'Körte' where id = 1	

Conn2:

4.
select * from Gyumolcs --1 Alma

Conn1:

5.
commit	

Conn2:

6.
select * from Gyumolcs -- 1 Körte
Commit

Látható, hogy a nyitott módosító tranzakció esetén (3) a módosítás előtt adatot kapjuk vissza (4), éppúgy, mint a SI esetén. Ha azonban a módosítás véglegesítésre került (5), az olvasó folyamat már a módosítás utáni állapotát látja a sornak (6). Ez az alapvető különbség az RCSI és az SI között.
RCSI esetén nincs konfliktus update esetén, a második módosító folyamat ugyanúgy blokkolódik, mint SI esetén, azonban az első író commitja után a második is sikeresen végrehajtja az update-jét.
Azaz RCSI esetén számíthatunk fejbe vágott módosításokra, nem megismételhető olvasásokra és fantom sorokra. Cserébe viszont jelentősen kevesebb adat kerül a verziótárba, hisz nem kell olyan hosszú ideig megtartani a módosítás előtti állapotokat. Megint valamit-valamiért.
A verziótár más felhasználásai
A tempdb-ben kialakított verziótár elsődlegesen a Snapshot szintek kedvéért került kialakításra. Ám ha már elkészült, más szolgáltatást is építettek rá.
A triggerek már eddig is egyfajta verziózott módon működtek, hisz pl. a deleted tábla a sorok módosítás előtti állapotát mutatta. Ezt SQL Server 2000-ben a tranzakciós napló visszaolvasásával állították elő. A megoldás nagy hátránya volt, hogy az egyirányú írásra optimalizált tranzakciós naplót időnként vissza kellett olvasni, ami miatt megnövekedett a HDD fejmozgások száma, így lecsökkent a tranzakciós log írási sebessége.
SQL 2005-től kezdve a triggerek inserted és deleted virtuális tábláit a verziótár segítségével szintetizálják. Akkor is, ha egyik snapshot üzemmód sincs bekapcsolva. Ettől megszűnik a logra gyakorolt kedvezőtlen hatás, de megnő a tempdb terhelése.
A verziótár másik jelentősége az online indexműveleteknél jön a képbe. SQL 2005-től az indexműveleteket (create, drop, rebuild) az érintett tábla zárolása nélkül is végrehajtható, ezek az online műveletek. Például:

alter index all on Production.Product
rebuild with (online = on);

Amikor elkezdődik az új index felépítése a szerver belül “átvált” SNAPSHOT izolációs szintre, így a meglévő index adatait a tranzakció, az index építés kezdetéhez képest konzisztens módon képes átmásolni. Miközben építi fel az új, párhuzamos indexfát közben más kapcsolatokon keresztül módosíthatják az alaptáblát és az indexet is. A módosítások egyszerre módosítják az eredeti indexet és az épülőt is. A verziótároló a tempdb-ben közben szépen rögzít minden változást, így a módosítások nem kerülnek duplán átvezetésre.
Ami számunkra ebből kívülről látható lényeg, hogy az indexművelet alatt is elérhető a tábla és az index.
A Multiple Active Resultset (MARS) nevű harmadik technológia az, ami még kihasználja a verziótárolót, ám ennek működését most nem részletezem.

Verziótár takarítás, monitorozás

Ha indokolatlanul nagynak tűnik a tempdb vagy valamelyik snapshot szint bekapcsolása után szeretnénk látni mennyire intenzíven használja a szerver a verziótárat, többféle úton is kutakodhatunk.
Ha arra vagyunk kíváncsiak hány sor van a verziótárban, futtassuk le a következő parancsot:
select count(*) from sys.dm_tran_version_store
SI tranzakciók esetén a tranzakció végén, RCSI tranzakcióknál a select lefutása után már nincs szükség a módosított sorok verziótörténetére. Ezért percenként lefut egy háttérszál a szerverben, amely kitörli a már nem szükséges sorokat.
Ha a tempdb kifutna a számára behatárolt helyből, akkor a takarító azonnal elindul, hátha fel tud szabadítani elég helyet, így nem kell megnövelni az adatbázist. Tisztára olyan ez, mint a .NET Framework Garbage Collectora, csak az a memóriát söprögeti.
A Version Generation Rate és a Version Cleanup Rate teljesítményszámlálók segítségével durván láthatjuk mekkora a sürgés-forgás a tárban.
Az Update conflict ratio túl nagy értéke azt sugallja, lehet, hogy érdemes visszatérni a zárolós üzemmódhoz, mert túl sok az író folyamat.
A verziótár halála egy idétlen tranzakció, amely – általában hibakezelési hiba miatt – túl hosszú ideig fut. Azaz elfelejtik lezárni a tranzakciót, de az adatbáziskapcsolatot nyitva hagyják. Egy ilyen tranzakció a zárolásos üzemmód esetén hosszú blokkolási láncokat, így akár az egész adatbázisra épülő alkalmazás leállását okozhatta. Most nem áll meg az élet, de lehet, hogy a tempdb nagyon nagyra nő, és belassul az adatbázis. A Longest Transaction Running Time nevű számlálóban meg lehet nézni, van-e valaki beragadva. Ha itt meglátunk mondjuk 1800 másodpercet, azaz fél órát, miközben tudjuk, hogy a tranzakcióink 1 percen belül lefutnak, akkor sejtjük, hogy valaki beragadt. A bűnöst a következő lekérdezéssel érhetjük tetten:

select elapsed_time_seconds, session_id 
from sys.dm_tran_active_snapshot_database_transactions
elapsed_time_seconds session_id
-------------------- -----------
848                  52

Az 52-es session (kapcsolat) már 848 másodperce tollászkodik, ha nem indokolt a munkája, kilőhető.
Használjuk vagy sem?

Zárásul nézzük meg, milyen szempontok alapján tudunk választani a kétféle snapshot elszigetelés között, illetve mire számítsunk, ha úgy általában átállunk a zárolós üzemmódról valamelyik verziós üzemmódra.

Általánosságban a RCSI-on érdemes először elgondolkodni, mert:
• Kevesebb helyet igényel a tempdb-ben mint a SI
• Használható elosztott tranzakciókban is, a SI nem
• Nem lesznek update konfliktusok
• Nem kell átírni az alkalmazásokat. Egyszerűen be kell kapcsolni az adatbázison RCSI-t, és máris minden alkalmazás élvezi az előnyeit.

Mikor érdemes ezek után az SI-t bevetni?
• Ha általában kevés a párhuzamosan futó módosító folyamat, így kicsi az update konfliktusok valószínűsége
• Olyan lekérdezéseket, riportokat kell futtatni, amelyeknek fontos az időpont-konzisztencia. Magyarul ha egy tranzakcióban többször is át kell gyúrni az adatokat és fontos, hogy közben az adathalmazunk stabil legyen, akkor ezt csak a SI tudja biztosítani, a RCSI nem. Például a tranzakcióban először aggregáló lekérdezéseket futtatunk egy táblán, aztán a részletes adatokat válogatjuk le. SI nélkül inkonzisztens lehetne a kimenet, azaz a részletes adatok nem vágnának egybe az aggregált eredményekkel.

Vessük most össze a verziós üzemmódokat a zárolós üzemmódokkal.

A verziózás előnyei a zárolással szemben:
• A select nem rak csak olvasható (shared) zárakat a táblára, így az írók és az olvasók nem akadályozzák egymást (ez a legfőbb érv a verziózás mellett)
• A selectek időben konzisztens képet adnak vissza (SI és RCSI esetén másképp, lásd korábban)
• Sokkal kevesebb zárat (lockot) kell nyilvántartani a szervernek, ami jelentős erőforrást takarít meg
• Kevesebb zár-eszkaláció történik (amikor sok sor vagy lap szintű zárat átkonvertál tábla szintű zárrá a szerver, hogy erőforrásokat szabadítson fel)
• Kisebb a deadlockok valószínűsége

Természetesen vannak hátrányai is a verziózásnak, másként automatikusan ezt használnánk. Ezek a következők:
• A selectek lelassulhatnak, ha hosszú verzió-láncokat kell végignézni sok nyitott tranzakció miatt
• A tempdb-ben helyet kell neki biztosítani
• Az adatmódosító műveletek verziókat fognak generálni a verziótárolóban, még akkor is, ha nincs közvetlenül rájuk szükség. Azaz lassulnak az adatmódosítások.
• Minden sor ami a verziózás bekapcsolása után módosult 14 bájttal hosszabb lesz (mutató a verziótár megfelelő bugyrára)
• Az update lassabb lesz a verziótárolás miatt
• SI esetén az update konfliktusba kerül párhuzamos folyamatok update-jeivel, amit le kell kezelni az alkalmazásnak
• Ügyelni kell a tempdb méretére, nehogy megszaladjon.

Összefoglalva elmondható, hogy a snapshot elszigetelési szintek megjelenése hatalmas fegyvertény, amellyel sok párhuzamosan működő rendszer működését lehet drámaian felgyorsítani, illetve nagyban megkönnyíti olyan alkalmazások átírását más adatbáziskezelő rendszerről, amely verziózással működik.

3 Responses to “Snapshot elszigetelési szint a SQL Server 2005-től”

  1. Geri Says:

    Nagyon jó írás, egyszerűen magyaráz el egy bonyolult problémát.
    Köszönjük, az emberiség nevében. :)

  2. Soczó Zsolt Says:

    Kösz, néha fordítva szokott sikerülni. :)

  3. Soci (Soczó Zsolt) szakmai blogja » Blog Archive » Snapshot isolation level Says:

    […] korábbi, 2008-ban írt cikkemet emeltem be ide, mivel láttam, hogy többen meg akarták nézni nálam, de nem sikerült, mert a technetre mutató […]