Jegyzet
Az oldalhoz való hozzáférés engedélyezést igényel. Próbálhatod be jelentkezni vagy könyvtárat váltani.
Az oldalhoz való hozzáférés engedélyezést igényel. Megpróbálhatod a könyvtár váltását.
A pillanatkép-elkülönítés növeli az egyidejűséget az OLTP-alkalmazások esetében.
A pillanatképek elkülönítésének és a sorok verziószámozásának ismertetése
A pillanatkép-elkülönítés engedélyezése után az egyes tranzakciók frissített sorverziói a tempdbmegmaradnak. Egy egyedi tranzakciósorozat-szám azonosítja az egyes tranzakciókat, és ezeket az egyedi számokat minden egyes sorverzióhoz rögzíti a rendszer. A tranzakció a legutóbbi sorverziókkal működik, amelyek sorszámmal rendelkeznek a tranzakció sorszáma előtt. A tranzakció megkezdése után létrehozott újabb sorverziókat a tranzakció figyelmen kívül hagyja.
A "pillanatkép" kifejezés azt a tényt tükrözi, hogy a tranzakció összes lekérdezése ugyanazt az adatbázis-verziót vagy pillanatképet látja az adatbázis állapotától függően, amikor a tranzakció megkezdődik. A pillanatkép-tranzakció mögöttes adatsorain vagy adatoldalain nem történik zárolás, ami lehetővé teszi más tranzakciók végrehajtását anélkül, hogy egy korábbi hiányos tranzakció blokkolta őket. Az adatokat módosító tranzakciók nem tiltják le az adatokat olvasott tranzakciókat, és az adatokat olvasott tranzakciók nem tiltják le az adatokat író tranzakciókat, mivel általában az SQL Server alapértelmezett READ COMMITTED elkülönítési szintje alatt lennének. Ez a nem blokkoló viselkedés jelentősen csökkenti az összetett tranzakciók holtpontjainak valószínűségét is.
A pillanatkép-izoláció optimista egyidejűségi modellt használ. Ha egy pillanatkép-tranzakció megpróbál módosításokat véglegesíteni a tranzakció kezdete óta megváltozott adatokon, a tranzakció vissza fog gördülni, és hibaüzenet jelenik meg. Ezt elkerülheti, ha UPDLOCK-tippeket használ a módosítandó adatokhoz hozzáférő SELECT utasításokhoz. További információ: "Zárolási tippek" az SQL Server Books Online-ban.
A pillanatkép-elkülönítést engedélyezni kell a ALLOW_SNAPSHOT_ISOLATION ON adatbázis beállításának beállításával, mielőtt a tranzakciókban használva lenne. Ez aktiválja a sorverziók ideiglenes adatbázisban való tárolásának mechanizmusát (tempdb). Engedélyeznie kell a pillanatkép-elkülönítést minden olyan adatbázisban, amely az Transact-SQL ALTER DATABASE utasítással használja. Ebben a tekintetben a pillanatképek elkülönítése eltér a READ COMMITTED, REPEATABLE READ, SERIALIZABLE és READ UNCOMMITTED hagyományos elkülönítési szintjeitől, amelyek nem igényelnek konfigurációt. Az alábbi utasítások aktiválják a pillanatkép-elkülönítést, és lecserélik az alapértelmezett READ COMMITTED viselkedést a SNAPSHOT-ra:
ALTER DATABASE MyDatabase
SET ALLOW_SNAPSHOT_ISOLATION ON
ALTER DATABASE MyDatabase
SET READ_COMMITTED_SNAPSHOT ON
A READ_COMMITTED_SNAPSHOT ON beállítás beállítása lehetővé teszi a verziószámozott sorok elérését az alapértelmezett READ COMMITTED elkülönítési szint alatt. Ha a READ_COMMITTED_SNAPSHOT beállítás KI van kapcsolva, a verziószámozott sorok eléréséhez explicit módon be kell állítania az egyes munkamenetek pillanatfelvétel-izolációs szintet.
A párhuzamosság kezelése izolációs szintekkel
A Transact-SQL utasítás végrehajtásának elkülönítési szintje határozza meg annak zárolási és sorverzió-kezelési viselkedését. Az elkülönítési szint kapcsolatszintű hatókörrel rendelkezik, és a SET TRANSACTION ISOLATION LEVEL utasítással való kapcsolat beállítása után a kapcsolat lezárultáig vagy egy másik elkülönítési szint beállításáig érvényben marad. Ha egy kapcsolat lezárul, és visszakerül a készletbe, a rendszer megtartja az utolsó SET TRANSACTION ISOLATION LEVEL utasítás elkülönítési szintjét. A készletezett kapcsolatok későbbi újrafelhasználása a kapcsolat készletezésének időpontjában érvényben lévő elkülönítési szintet használja.
A kapcsolaton belül kiadott egyes lekérdezések tartalmazhatnak olyan zárolási tippeket, amelyek módosítják az elkülönítést egyetlen utasítás vagy tranzakció esetében, de nem befolyásolják a kapcsolat elkülönítési szintjét. A tárolt eljárásokban vagy függvényekben beállított elkülönítési szintek vagy zárolási tippek nem módosítják az őket meghívó kapcsolat elkülönítési szintjét, és csak a tárolt eljárás vagy függvényhívás időtartamára vannak érvényben.
Az SQL-92 szabványban meghatározott négy elkülönítési szint támogatott az SQL Server korai verzióiban:
A READ UNCOMMITTED a legkevésbé korlátozó elkülönítési szint, mivel figyelmen kívül hagyja a más tranzakciók által elhelyezett zárolásokat. A READ UNCOMMITTED alatt végrehajtott tranzakciók beolvashatják azokat a módosított adatértékeket, amelyeket más tranzakciók még nem véglegesítettek; ezeket "piszkos" olvasásoknak nevezzük.
A READ COMMITTED az SQL Server alapértelmezett elkülönítési szintje. Megakadályozza a piszkos olvasást, ha megadja, hogy az utasítások nem tudják beolvasni azokat az adatértékeket, amelyeket más tranzakciók módosítottak, de még nem véglegesítettek. Más tranzakciók továbbra is módosíthatják, beszúrhatják vagy törölhetik az adatokat az aktuális tranzakció egyes utasításainak végrehajtása között, ami nem megismételhető olvasásokat vagy "fantom" adatokat eredményez.
A REPEATABLE READ egy szigorúbb elkülönítési szint, mint a READ COMMITTED. Magában foglalja a READ COMMITTED-t, és azt is meghatározza, hogy más tranzakciók nem módosíthatják vagy törölhetik az aktuális tranzakció által beolvasott adatokat, amíg az aktuális tranzakció véglegesítésre nem került. Az egyidejűség alacsonyabb, mint a READ COMMITTED esetében, mivel az olvasási adatok megosztott zárai a tranzakció időtartamára tartva vannak, nem az egyes utasítások végén oldódnak fel.
A SZERIALIZABLE a legkorlátozóbb elkülönítési szint, mivel a kulcsok teljes tartományát zárolja, és a zárolásokat a tranzakció befejezéséig tárolja. Ez magában foglalja az ISMÉTELHETŐ OLVASÁST, és azt a korlátozást adja hozzá, hogy más tranzakciók nem szúrhatnak be új sorokat a tranzakció által beolvasott tartományokba, amíg a tranzakció be nem fejeződik.
További információkért tekintse meg a tranzakciózárolási és sorverziózás útmutatót.
Pillanatkép-elkülönítési szint bővítményei
Az SQL Server bővítményeket vezetett be az SQL-92 elkülönítési szintjeihez a SNAPSHOT elkülönítési szint bevezetésével és a READ COMMITTED további implementálásával. A READ_COMMITTED_SNAPSHOT elkülönítési szint transzparens módon helyettesítheti a READ COMMITTED elemet az összes tranzakció esetében.
A SNAPSHOT-elkülönítés azt határozza meg, hogy a tranzakción belül beolvasott adatok soha nem tükrözik a többi egyidejű tranzakció módosításait. A tranzakció a tranzakció kezdetekor létező adatsor-verziókat használja. Olvasáskor nem kerül zárolás az adatokra, így a PILLANATKÉP-tranzakciók nem tiltják le más tranzakciók adatírását. Az adatokat író tranzakciók nem akadályozzák a pillanatkép-tranzakciókat az adatok olvasásában. A funkció használatához engedélyeznie kell a pillanatképek elkülönítését a ALLOW_SNAPSHOT_ISOLATION adatbázis beállításával.
A READ_COMMITTED_SNAPSHOT adatbázis-beállítás határozza meg az alapértelmezett READ COMMITTED elkülönítési szint viselkedését, ha a pillanatkép-elkülönítés engedélyezve van az adatbázisban. Ha nem adja meg explicit módon a READ_COMMITTED_SNAPSHOT ON parancsot, a READ COMMITTED lesz alkalmazva minden implicit tranzakcióra. Ez ugyanazt a viselkedést eredményezi, mint a READ_COMMITTED_SNAPSHOT KI (alapértelmezett) beállítás. Ha READ_COMMITTED_SNAPSHOT KI van kapcsolva, az adatbázismotor megosztott zárolásokkal kényszeríti ki az alapértelmezett elkülönítési szintet. Ha a READ_COMMITTED_SNAPSHOT adatbázis beállítását BE értékre állítja, az adatbázismotor alapértelmezés szerint sorverziót és pillanatkép-elkülönítést használ az adatok védelméhez használt zárolások helyett.
A pillanatképek elkülönítése és a sorok verziószámozásának működése
Ha a SNAPSHOT elkülönítési szint engedélyezve van, minden sor frissítésekor az SQL Server adatbázismotor az eredeti sor másolatát tárolja tempdb, és hozzáad egy tranzakciósorozat-számot a sorhoz. A következő események sorozata következik be:
A rendszer új tranzakciót indít el, és hozzá van rendelve egy tranzakciósorozat-számhoz.
Az adatbázismotor beolvas egy sort a tranzakció során, és megkeresi a sorverziót a tempdb-ben, amelynek sorszáma a legközelebbi és alacsonyabb a tranzakció sorszámánál.
Az adatbázismotor ellenőrzi, hogy a tranzakcióütemezési szám nem szerepel-e a pillanatkép-tranzakció indításakor aktív, nem véglegesített tranzakciók tranzakcióütemezési számainak listájában.
A tranzakció a sor verzióját olvassa be a tempdb-ből, amely a tranzakció kezdetekor volt aktuális. A tranzakció elindítása után nem jelenik meg új sorok beszúrása, mert ezek a sorszámértékek magasabbak lesznek a tranzakcióütemezési szám értékénél.
Az aktuális tranzakció a tranzakció elindítása után törölt sorokat fogja látni, mivel a tempdb sorverziója alacsonyabb sorszámértékkel fog megjelenni.
A pillanatkép-elkülönítés nettó hatása az, hogy a tranzakció úgy látja az összes adatot, ahogyan az a tranzakció elején létezett, anélkül, hogy bármilyen zárolást alkalmazna a mögöttes táblákon. Ez teljesítménybeli javulást eredményezhet olyan helyzetekben, amikor versengés történik.
A pillanatkép-tranzakció mindig optimista egyidejűség-vezérlést használ, elkerülve azokat a zárolásokat, amelyek meggátolnák más tranzakciók sorok frissítését. Ha egy pillanatkép-tranzakció megpróbál véglegesíteni egy frissítést egy olyan sorra, amely a tranzakció elindítása után módosult, a rendszer visszaállítja a tranzakciót, és hibaüzenet jelenik meg.
Pillanatkép-elkülönítéssel való munkavégzés az ADO.NET-ben
Az ADO.NET támogatja a pillanatkép izolációt a SqlTransaction osztályon keresztül. Ha egy adatbázis engedélyezve van a pillanatképek elkülönítéséhez, de a READ_COMMITTED_SNAPSHOT nincs bekapcsolva, kezdeményeznie kell egy SqlTransaction a IsolationLevel.Snapshot enumerációs érték használatával a BeginTransaction metódus meghívásakor. Ez a kódrészlet feltételezi, hogy a kapcsolat egy nyitott SqlConnection objektum.
SqlTransaction sqlTran =
connection.BeginTransaction(IsolationLevel.Snapshot);
Példa
Az alábbi példa bemutatja, hogyan viselkednek a különböző elkülönítési szintek a zárolt adatok elérésének megkísérlésével, és nem az éles kódban való használatra szolgálnak.
A kód az SQL Server AdventureWorks mintaadatbázishoz csatlakozik, és létrehoz egy TestSnapshot nevű táblát, és beszúr egy adatsort. A kód az ALTER DATABASE Transact-SQL utasítással kapcsolja be az adatbázis pillanatkép-elkülönítését, de nem állítja be a READ_COMMITTED_SNAPSHOT beállítást, így az alapértelmezett READ COMMITTED elkülönítési szintű viselkedés érvényben marad. A kód ezután a következő műveleteket hajtja végre:
Az sqlTransaction1 tranzakció megkezdődik, de nem fejeződik be, amely a SZERIALIZÁLHATÓ elkülönítési szintet használja a tranzakció frissítésének megkezdéséhez. Ennek hatására a táblázat zárolásra kerül.
Megnyitja a második kapcsolatot, és elindít egy második tranzakciót a SNAPSHOT elkülönítési szinttel a TestSnapshot táblában lévő adatok beolvasásához. Mivel a pillanatkép-elkülönítés engedélyezve van, ez a tranzakció beolvassa az sqlTransaction1 elindítása előtt létező adatokat.
Megnyitja a harmadik kapcsolatot, és elindít egy tranzakciót a READ COMMITTED elkülönítési szinttel, hogy megpróbálja beolvasni az adatokat a táblában. Ebben az esetben a kód nem tudja beolvasni az adatokat, mert nem tudja beolvasni az első tranzakcióban a táblára helyezett zárolásokat, és időtúllépést jelez. Ugyanez az eredmény akkor fordul elő, ha a REPEATABLE READ és a SZERIALIZÁLHATÓ elkülönítési szinteket használják, mert ezek az elkülönítési szintek nem tudják beolvasni az első tranzakcióban elhelyezett zárolásokat.
Megnyitja a negyedik kapcsolatot, és elindít egy tranzakciót a READ UNCOMMITTED elkülönítési szinttel, amely az sqlTransaction1 nem véglegesített értékének piszkos olvasását hajtja végre. Előfordulhat, hogy ez az érték valójában nem létezik az adatbázisban, ha az első tranzakció nincs véglegesítése.
Visszaállítja az első tranzakciót, és a rendszertisztítás érdekében törli a TestSnapshot táblát, valamint kikapcsolja a pillanatkép-elkülönítést az AdventureWorks adatbázisban.
Jegyzet
Az alábbi példák ugyanazt a kapcsolati sztringet használják, ha a kapcsolatkészletezés ki van kapcsolva. Ha egy kapcsolat össze van állítva, az elkülönítési szint alaphelyzetbe állítása nem állítja vissza az elkülönítési szintet a kiszolgálón. Ennek eredményeképpen az azonos készletezett belső kapcsolatot használó későbbi kapcsolatok a készletezett kapcsolat elkülönítési szintjeivel kezdődnek. A kapcsolatkészletezés kikapcsolásának másik lehetősége az elkülönítési szint kifejezetten az egyes kapcsolatokhoz való beállítása.
using Microsoft.Data.SqlClient;
class Program
{
static void Main()
{
// Assumes GetConnectionString returns a valid connection string
// where pooling is turned off by setting Pooling=False;.
string connectionString = GetConnectionString();
using (SqlConnection connection1 = new SqlConnection(connectionString))
{
// Drop the TestSnapshot table if it exists
connection1.Open();
SqlCommand command1 = connection1.CreateCommand();
command1.CommandText = "IF EXISTS "
+ "(SELECT * FROM sys.tables WHERE name=N'TestSnapshot') "
+ "DROP TABLE TestSnapshot";
try
{
command1.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
// Enable Snapshot isolation
command1.CommandText =
"ALTER DATABASE AdventureWorks SET ALLOW_SNAPSHOT_ISOLATION ON";
command1.ExecuteNonQuery();
// Create a table named TestSnapshot and insert one row of data
command1.CommandText =
"CREATE TABLE TestSnapshot (ID int primary key, valueCol int)";
command1.ExecuteNonQuery();
command1.CommandText =
"INSERT INTO TestSnapshot VALUES (1,1)";
command1.ExecuteNonQuery();
// Begin, but do not complete, a transaction to update the data
// with the Serializable isolation level, which locks the table
// pending the commit or rollback of the update. The original
// value in valueCol was 1, the proposed new value is 22.
SqlTransaction transaction1 =
connection1.BeginTransaction(IsolationLevel.Serializable);
command1.Transaction = transaction1;
command1.CommandText =
"UPDATE TestSnapshot SET valueCol=22 WHERE ID=1";
command1.ExecuteNonQuery();
// Open a second connection to AdventureWorks
using (SqlConnection connection2 = new SqlConnection(connectionString))
{
connection2.Open();
// Initiate a second transaction to read from TestSnapshot
// using Snapshot isolation. This will read the original
// value of 1 since transaction1 has not yet committed.
SqlCommand command2 = connection2.CreateCommand();
SqlTransaction transaction2 =
connection2.BeginTransaction(IsolationLevel.Snapshot);
command2.Transaction = transaction2;
command2.CommandText =
"SELECT ID, valueCol FROM TestSnapshot";
SqlDataReader reader2 = command2.ExecuteReader();
while (reader2.Read())
{
Console.WriteLine("Expected 1,1 Actual "
+ reader2.GetValue(0).ToString()
+ "," + reader2.GetValue(1).ToString());
}
transaction2.Commit();
}
// Open a third connection to AdventureWorks and
// initiate a third transaction to read from TestSnapshot
// using ReadCommitted isolation level. This transaction
// will not be able to view the data because of
// the locks placed on the table in transaction1
// and will time out after 4 seconds.
// You would see the same behavior with the
// RepeatableRead or Serializable isolation levels.
using (SqlConnection connection3 = new SqlConnection(connectionString))
{
connection3.Open();
SqlCommand command3 = connection3.CreateCommand();
SqlTransaction transaction3 =
connection3.BeginTransaction(IsolationLevel.ReadCommitted);
command3.Transaction = transaction3;
command3.CommandText =
"SELECT ID, valueCol FROM TestSnapshot";
command3.CommandTimeout = 4;
try
{
SqlDataReader sqldatareader3 = command3.ExecuteReader();
while (sqldatareader3.Read())
{
Console.WriteLine("You should never hit this.");
}
transaction3.Commit();
}
catch (Exception ex)
{
Console.WriteLine("Expected timeout expired exception: "
+ ex.Message);
transaction3.Rollback();
}
}
// Open a fourth connection to AdventureWorks and
// initiate a fourth transaction to read from TestSnapshot
// using the ReadUncommitted isolation level. ReadUncommitted
// will not hit the table lock, and will allow a dirty read
// of the proposed new value 22 for valueCol. If the first
// transaction rolls back, this value will never actually have
// existed in the database.
using (SqlConnection connection4 = new SqlConnection(connectionString))
{
connection4.Open();
SqlCommand command4 = connection4.CreateCommand();
SqlTransaction transaction4 =
connection4.BeginTransaction(IsolationLevel.ReadUncommitted);
command4.Transaction = transaction4;
command4.CommandText =
"SELECT ID, valueCol FROM TestSnapshot";
SqlDataReader reader4 = command4.ExecuteReader();
while (reader4.Read())
{
Console.WriteLine("Expected 1,22 Actual "
+ reader4.GetValue(0).ToString()
+ "," + reader4.GetValue(1).ToString());
}
transaction4.Commit();
}
// Roll back the first transaction
transaction1.Rollback();
}
// CLEANUP
// Delete the TestSnapshot table and set
// ALLOW_SNAPSHOT_ISOLATION OFF
using (SqlConnection connection5 = new SqlConnection(connectionString))
{
connection5.Open();
SqlCommand command5 = connection5.CreateCommand();
command5.CommandText = "DROP TABLE TestSnapshot";
SqlCommand command6 = connection5.CreateCommand();
command6.CommandText =
"ALTER DATABASE AdventureWorks SET ALLOW_SNAPSHOT_ISOLATION OFF";
try
{
command5.ExecuteNonQuery();
command6.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Console.WriteLine("Done!");
}
static private string GetConnectionString()
{
// To avoid storing the connection string in your code,
// you can retrieve it from a configuration file, using the
// System.Configuration.ConfigurationSettings.AppSettings property
return "Data Source=localhost;Initial Catalog=AdventureWorks;"
+ "Integrated Security=SSPI";
}
}
Példa
Az alábbi példa a pillanatképek elkülönítésének viselkedését mutatja be az adatok módosításakor. A kód a következő műveleteket hajtja végre:
Csatlakozik az AdventureWorks mintaadatbázishoz, és lehetővé teszi a PILLANATKÉP-elkülönítést.
Létrehoz egy TestSnapshotUpdate nevű táblát, és három sor mintaadatot szúr be.
Az sqlTransaction1 SNAPSHOT izolációt használva kezdődik, de nem fejeződik be. A tranzakció három adatsort jelöl ki.
Létrehoz egy második SqlConnection- kapcsolatot az AdventureWorks adatbázishoz, és létrehoz egy második tranzakciót a READ COMMITTED izolációs szinttel, amely frissíti az sqlTransaction1-ben kiválasztott sorok egyikének értékét.
Véglegesíti az sqlTransaction2-t.
Visszatér az sqlTransaction1 fájlhoz, és megpróbálja frissíteni azt a sort, amelyet az sqlTransaction1 már véglegesített. A rendszer 3960-ás hibát jelez, és az sqlTransaction1 automatikusan vissza lesz állítva. A SqlException.Number és SqlException.Message jelennek meg a Konzol ablakban.
Végrehajtja a tisztító kódot a pillanatfelvétel izoláció kikapcsolásához az AdventureWorks-ben, és törli a TestSnapshotUpdate táblát.
using Microsoft.Data.SqlClient; using System.Data.Common; class Program { static void Main() { // Assumes GetConnectionString returns a valid connection string // where pooling is turned off by setting Pooling=False;. string connectionString = GetConnectionString(); using (SqlConnection connection1 = new SqlConnection(connectionString)) { connection1.Open(); SqlCommand command1 = connection1.CreateCommand(); // Enable Snapshot isolation in AdventureWorks command1.CommandText = "ALTER DATABASE AdventureWorks SET ALLOW_SNAPSHOT_ISOLATION ON"; try { command1.ExecuteNonQuery(); Console.WriteLine( "Snapshot Isolation turned on in AdventureWorks."); } catch (Exception ex) { Console.WriteLine("ALLOW_SNAPSHOT_ISOLATION ON failed: {0}", ex.Message); } // Create a table command1.CommandText = "IF EXISTS " + "(SELECT * FROM sys.tables " + "WHERE name=N'TestSnapshotUpdate')" + " DROP TABLE TestSnapshotUpdate"; command1.ExecuteNonQuery(); command1.CommandText = "CREATE TABLE TestSnapshotUpdate " + "(ID int primary key, CharCol nvarchar(100));"; try { command1.ExecuteNonQuery(); Console.WriteLine("TestSnapshotUpdate table created."); } catch (Exception ex) { Console.WriteLine("CREATE TABLE failed: {0}", ex.Message); } // Insert some data command1.CommandText = "INSERT INTO TestSnapshotUpdate VALUES (1,N'abcdefg');" + "INSERT INTO TestSnapshotUpdate VALUES (2,N'hijklmn');" + "INSERT INTO TestSnapshotUpdate VALUES (3,N'opqrstuv');"; try { command1.ExecuteNonQuery(); Console.WriteLine("Data inserted TestSnapshotUpdate table."); } catch (Exception ex) { Console.WriteLine(ex.Message); } // Begin, but do not complete, a transaction // using the Snapshot isolation level. SqlTransaction transaction1 = null; try { transaction1 = connection1.BeginTransaction(IsolationLevel.Snapshot); command1.CommandText = "SELECT * FROM TestSnapshotUpdate WHERE ID BETWEEN 1 AND 3"; command1.Transaction = transaction1; command1.ExecuteNonQuery(); Console.WriteLine("Snapshot transaction1 started."); // Open a second Connection/Transaction to update data // using ReadCommitted. This transaction should succeed. using (SqlConnection connection2 = new SqlConnection(connectionString)) { connection2.Open(); SqlCommand command2 = connection2.CreateCommand(); command2.CommandText = "UPDATE TestSnapshotUpdate SET CharCol=" + "N'New value from Connection2' WHERE ID=1"; SqlTransaction transaction2 = connection2.BeginTransaction(IsolationLevel.ReadCommitted); command2.Transaction = transaction2; try { command2.ExecuteNonQuery(); transaction2.Commit(); Console.WriteLine( "transaction2 has modified data and committed."); } catch (SqlException ex) { Console.WriteLine(ex.Message); transaction2.Rollback(); } finally { transaction2.Dispose(); } } // Now try to update a row in Connection1/Transaction1. // This transaction should fail because Transaction2 // succeeded in modifying the data. command1.CommandText = "UPDATE TestSnapshotUpdate SET CharCol=" + "N'New value from Connection1' WHERE ID=1"; command1.Transaction = transaction1; command1.ExecuteNonQuery(); transaction1.Commit(); Console.WriteLine("You should never see this."); } catch (SqlException ex) { Console.WriteLine("Expected failure for transaction1:"); Console.WriteLine(" {0}: {1}", ex.Number, ex.Message); } finally { transaction1.Dispose(); } } // CLEANUP: // Turn off Snapshot isolation and delete the table using (SqlConnection connection3 = new SqlConnection(connectionString)) { connection3.Open(); SqlCommand command3 = connection3.CreateCommand(); command3.CommandText = "ALTER DATABASE AdventureWorks SET ALLOW_SNAPSHOT_ISOLATION OFF"; try { command3.ExecuteNonQuery(); Console.WriteLine( "CLEANUP: Snapshot isolation turned off in AdventureWorks."); } catch (Exception ex) { Console.WriteLine("CLEANUP FAILED: {0}", ex.Message); } command3.CommandText = "DROP TABLE TestSnapshotUpdate"; try { command3.ExecuteNonQuery(); Console.WriteLine("CLEANUP: TestSnapshotUpdate table deleted."); } catch (Exception ex) { Console.WriteLine("CLEANUP FAILED: {0}", ex.Message); } } Console.WriteLine("Done"); Console.ReadLine(); } static private string GetConnectionString() { // To avoid storing the connection string in your code, // you can retrieve it from a configuration file, using the // System.Configuration.ConfigurationSettings.AppSettings property return "Data Source=(local);Initial Catalog=AdventureWorks;" + "Integrated Security=SSPI;Pooling=false"; } }
Zárolási tippek használata pillanatkép-elkülönítéssel
Az előző példában az első tranzakció kiválasztja az adatokat, egy második tranzakció pedig frissíti az adatokat, mielőtt az első tranzakció befejeződhet, ami frissítési ütközést okoz, amikor az első tranzakció megpróbálja frissíteni ugyanazt a sort. Csökkentheti a frissítési ütközések esélyét a hosszú ideig futó pillanatkép-tranzakciókban, ha a tranzakció elején zárolási tippeket ad meg. A következő SELECT utasítás az UPDLOCK-tipp használatával zárolja a kijelölt sorokat:
SELECT * FROM TestSnapshotUpdate WITH (UPDLOCK)
WHERE PriKey BETWEEN 1 AND 3
Az UPDLOCK zárolási tipp használatával zárolhatja azokat a sorokat, amelyek megpróbálják frissíteni a sorokat addig, amíg az első tranzakció be nem fejeződik. Ez garantálja, hogy a kijelölt sorok nem ütköznek a tranzakció későbbi frissítésekor. Lásd: "Zárolási tippek" az SQL Server Books Online-ban.
Ha az alkalmazásnak sok ütközése van, előfordulhat, hogy a pillanatképek elkülönítése nem a legjobb választás. Tippeket csak akkor szabad használni, ha valóban szükség van rá. Az alkalmazást nem úgy kell megtervezni, hogy a működéshez folyamatosan a zárolási tippekre támaszkodjon.