Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of mappen te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen om mappen te wijzigen.
Isolatie van momentopnamen verbetert gelijktijdigheid voor OLTP-toepassingen.
Inzicht in snapshot-isolatie en rijversiebeheer
Zodra isolatie van momentopnamen is ingeschakeld, worden bijgewerkte rijversies voor elke transactie onderhouden in tempdb. Een uniek transactiereeksnummer identificeert elke transactie en deze unieke nummers worden vastgelegd voor elke rijversie. De transactie maakt gebruik van de meest recente rijversies met een volgnummer dat voorafgaat aan het volgnummer van de transactie. Nieuwere rijversies die zijn gemaakt nadat de transactie is gestart, worden genegeerd door de transactie.
De term 'momentopname' weerspiegelt het feit dat alle query's in de transactie dezelfde versie of momentopname van de database zien, op basis van de status van de database op het moment waarop de transactie begint. Er worden geen vergrendelingen verkregen op de onderliggende gegevensrijen of gegevenspagina's in een momentopnametransactie, waardoor andere transacties kunnen worden uitgevoerd zonder dat ze worden geblokkeerd door een eerdere onvolledige transactie. Transacties die gegevens wijzigen, blokkeren transacties die gegevens lezen niet en transacties die gegevens lezen, blokkeren geen transacties die gegevens schrijven, omdat ze normaal gesproken onder het standaard isolementatieniveau READ COMMITTED in SQL Server zouden staan. Dit niet-blokkerende gedrag vermindert ook de kans op impasses voor complexe transacties aanzienlijk.
Isolatie van momentopnamen maakt gebruik van een optimistisch gelijktijdigheidsmodel. Als een momentopnametransactie probeert wijzigingen door te voeren aan gegevens die zijn gewijzigd sinds de transactie is gestart, wordt de transactie teruggedraaid en wordt er een fout gegenereerd. U kunt dit voorkomen door UPDLOCK-hints te gebruiken voor SELECT-instructies die toegang hebben tot gegevens die moeten worden gewijzigd. Zie "Locking Hints" in SQL Server Books Online voor meer informatie.
Isolatie van momentopnamen moet worden ingeschakeld door de optie ALLOW_SNAPSHOT_ISOLATION ON-database in te stellen voordat deze wordt gebruikt in transacties. Hiermee wordt het mechanisme voor het opslaan van rijversies in de tijdelijke database (tempdb-) geactiveerd. U moet isolatie van momentopnamen inschakelen in elke database die deze gebruikt met de instructie Transact-SQL ALTER DATABASE. In dit opzicht verschilt de isolatie van momentopnamen van de traditionele isolatieniveaus van READ COMMIT, REPEATABLE READ, SERIALIZABLE en READ UNCOMMITTED, waarvoor geen configuratie is vereist. Met de volgende instructies wordt isolatie van momentopnamen geactiveerd en wordt het standaardgedrag READ COMMIT vervangen door SNAPSHOT:
ALTER DATABASE MyDatabase
SET ALLOW_SNAPSHOT_ISOLATION ON
ALTER DATABASE MyDatabase
SET READ_COMMITTED_SNAPSHOT ON
Als u de optie READ_COMMITTED_SNAPSHOT ON instelt, krijgt u toegang tot versies van rijen onder het standaard READ COMMITTED isolatieniveau. Als de optie READ_COMMITTED_SNAPSHOT is ingesteld op UIT, moet u het isolatieniveau voor momentopnamen voor elke sessie expliciet instellen om toegang te krijgen tot versierijen.
Gelijktijdigheid beheren met isolatieniveaus
Het isolatieniveau waaronder een Transact-SQL instructie wordt uitgevoerd, bepaalt het vergrendelings- en rijversiegedrag. Een isolatieniveau heeft een verbindingsbreed bereik en zodra deze is ingesteld voor een verbinding met de instructie SET TRANSACTION ISOLATION LEVEL, blijft dit van kracht totdat de verbinding is gesloten of een ander isolatieniveau is ingesteld. Wanneer een verbinding wordt gesloten en teruggegeven aan de pool, blijft het isolatieniveau van de laatste verklaring SET TRANSACTION ISOLATION LEVEL behouden. Volgende verbindingen die een gegroepeerde verbinding hergebruiken, gebruiken het isolatieniveau dat van kracht was op het moment dat de verbinding is gegroepeerd.
Afzonderlijke query's die binnen een verbinding worden uitgegeven, kunnen vergrendelingshints bevatten die de isolatie voor één instructie of transactie wijzigen, maar geen invloed hebben op het isolatieniveau van de verbinding. Isolatieniveaus of vergrendelingshints die zijn ingesteld in opgeslagen procedures of functies wijzigen niet het isolatieniveau van de verbinding die deze aanroept en zijn alleen van kracht voor de duur van de opgeslagen procedure of functieaanroep.
Vier isolatieniveaus die zijn gedefinieerd in de SQL-92-standaard, worden ondersteund in vroege versies van SQL Server:
READ UNCOMMITTED is het minst beperkende isolatieniveau omdat hiermee vergrendelingen worden genegeerd die door andere transacties worden geplaatst. Transacties die worden uitgevoerd onder READ UNCOMMITTED kunnen gewijzigde gegevenswaarden lezen die nog niet zijn vastgelegd door andere transacties; deze worden 'vuile' leesbewerkingen genoemd.
READ COMMIT is het standaardisolatieniveau voor SQL Server. Het voorkomt vuile leesbewerkingen door op te geven dat instructies geen gegevenswaarden kunnen lezen die zijn gewijzigd, maar die nog niet zijn doorgevoerd door andere transacties. Andere transacties kunnen nog steeds gegevens wijzigen, invoegen of verwijderen tussen uitvoeringen van afzonderlijke instructies binnen de huidige transactie, wat resulteert in niet-herhaalbare lees- of fantoomgegevens.
REPEATABLE READ is een strikter isolatieniveau dan READ COMMITTED. Het omvat READ COMMITTED en geeft bovendien aan dat er geen andere transacties gegevens kunnen wijzigen of verwijderen die door de huidige transactie zijn gelezen totdat de huidige transactie doorvoert. Gelijktijdigheid is lager dan voor READ COMMITTED omdat gedeelde vergrendelingen op leesgegevens voor de duur van de transactie worden vastgehouden in plaats van aan het einde van elke instructie te worden vrijgegeven.
SERIALIZABLE is het meest beperkende isolatieniveau, omdat het hele reeks sleutels vergrendelt en de vergrendelingen vasthoudt totdat de transactie is voltooid. Het omvat HERHAALBARE LEESBEWERKING en voegt de beperking toe dat andere transacties geen nieuwe rijen kunnen invoegen in reeksen die door de transactie zijn gelezen totdat de transactie is afgerond.
Voor meer informatie raadpleeg de Handleiding voor transactievergrendeling en rijversiebeheer.
Uitbreidingen van snapshot-isolatieniveau
SQL Server heeft uitbreidingen geïntroduceerd voor de SQL-92-isolatieniveaus met de introductie van het isolatieniveau SNAPSHOT en een extra implementatie van READ COMMITTED. Het READ_COMMITTED_SNAPSHOT isolatieniveau kan READ COMMITTED transparant vervangen voor alle transacties.
Momentopname-isolatie specificeert dat gegevens die binnen een transactie worden gelezen, nooit wijzigingen zullen weergeven die door andere gelijktijdige transacties zijn aangebracht. De transactie maakt gebruik van de gegevensrijversies die bestaan wanneer de transactie begint. Er worden geen vergrendelingen op de gegevens geplaatst wanneer deze worden gelezen, dus MOMENTOPNAME-transacties blokkeren niet dat andere transacties gegevens schrijven. Transacties die gegevens schrijven, blokkeren niet dat momentopnametransacties gegevens lezen. U moet isolatie van momentopnamen inschakelen door de optie ALLOW_SNAPSHOT_ISOLATION database in te stellen om deze te kunnen gebruiken.
De READ_COMMITTED_SNAPSHOT-databaseoptie bepaalt het gedrag van het standaardniveau READ COMMITTED-isolatie wanneer momentopname-isolatie is ingeschakeld in een database. Als u niet expliciet READ_COMMITTED_SNAPSHOT ON opgeeft, wordt READ COMMITTED toegepast op alle impliciete transacties. Dit produceert hetzelfde gedrag als het instellen van READ_COMMITTED_SNAPSHOT UIT (de standaardinstelling). Wanneer READ_COMMITTED_SNAPSHOT UIT van kracht is, gebruikt de database-engine gedeelde vergrendelingen om het standaardisolatieniveau af te dwingen. Als u de optie READ_COMMITTED_SNAPSHOT database instelt op AAN, gebruikt de database-engine rijversiebeheer en isolatie van momentopnamen als standaard, in plaats van vergrendelingen te gebruiken om de gegevens te beveiligen.
Hoe isolatie van momentopnamen en rijversiebeheer werken
Wanneer het isolatieniveau SNAPSHOT is ingeschakeld, wordt telkens wanneer een rij wordt bijgewerkt, een kopie van de oorspronkelijke rij opgeslagen in tempdb-en wordt er een transactiereeksnummer aan de rij toegevoegd. Hier volgt een reeks gebeurtenissen die zich voordoen:
Er wordt een nieuwe transactie gestart en er wordt een transactiereeksnummer toegewezen.
De database-engine leest een rij in de transactie en haalt de rijversie op uit tempdb waarvan het volgnummer het dichtst bij en lager is dan het transactiereeksnummer.
De database-engine controleert of het transactiereeksnummer zich niet in de lijst met transactiereeksnummers bevindt van de niet-doorgevoerde transacties die actief zijn wanneer de momentopnametransactie is gestart.
De transactie leest de versie van de rij uit tempdb die vanaf het begin van de transactie actueel was. Er worden geen nieuwe rijen ingevoegd nadat de transactie is gestart, omdat deze reeksnummerwaarden hoger zijn dan de waarde van het transactiereeksnummer.
De huidige transactie ziet rijen die zijn verwijderd nadat de transactie is begonnen, omdat er een rijversie in tempdb is met een lagere reeksnummerwaarde.
Het netto-effect van isolatie van momentopnamen is dat de transactie alle gegevens ziet zoals deze aan het begin van de transactie bestonden, zonder vergrendelingen op de onderliggende tabellen te respecteren of te plaatsen. Dit kan leiden tot prestatieverbeteringen in situaties waarin sprake is van conflicten.
Een momentopnametransactie maakt altijd gebruik van optimistische gelijktijdigheidscontrole, waarbij vergrendelingen worden vermeden die zouden voorkomen dat andere transacties rijen bijwerken. Als een momentopnametransactie probeert een update door te voeren naar een rij die is gewijzigd nadat de transactie is gestart, wordt de transactie teruggedraaid en wordt er een fout gegenereerd.
Werken met isolatie van momentopnamen in ADO.NET
Isolatie van momentopnamen wordt ondersteund in ADO.NET door de SqlTransaction klasse. Als een database is ingeschakeld voor isolatie van momentopnamen, maar niet is geconfigureerd voor READ_COMMITTED_SNAPSHOT ON, moet u een SqlTransaction initiëren met behulp van de IsolationLevel.Snapshot opsommingswaarde bij het aanroepen van de methode BeginTransaction. In dit codefragment wordt ervan uitgegaan dat de verbinding een geopend SqlConnection-object is.
SqlTransaction sqlTran =
connection.BeginTransaction(IsolationLevel.Snapshot);
Example
In het volgende voorbeeld ziet u hoe de verschillende isolatieniveaus zich gedragen door toegang te krijgen tot vergrendelde gegevens en het is niet bedoeld om te worden gebruikt in productiecode.
De code maakt verbinding met de AdventureWorks-voorbeelddatabase in SQL Server en maakt een tabel met de naam TestSnapshot en voegt één rij met gegevens in. De code maakt gebruik van de instructie ALTER DATABASE Transact-SQL om isolatie van momentopnamen voor de database in te schakelen, maar stelt de optie READ_COMMITTED_SNAPSHOT niet in, waardoor het standaardgedrag op isolatieniveau READ COMMITTED van kracht blijft. De code voert vervolgens de volgende acties uit:
Het begint met sqlTransaction1, maar voltooit het niet; sqlTransaction1 maakt gebruik van het isolatieniveau SERIALIZABLE om een updatetransactie te starten. Dit heeft het effect van het vergrendelen van de tabel.
Er wordt een tweede verbinding geopend en er wordt een tweede transactie gestart met behulp van het isolatieniveau SNAPSHOT om de gegevens in de TestSnapshot-tabel te lezen. Omdat isolatie van momentopnamen is ingeschakeld, kan deze transactie de gegevens lezen die bestonden voordat sqlTransaction1 werd gestart.
Er wordt een derde verbinding geopend en er wordt een transactie gestart met behulp van het isolatieniveau READ COMMITTED om de gegevens in de tabel te lezen. In dit geval kan de code de gegevens niet lezen omdat deze de vergrendelingen die in de tabel in de eerste transactie zijn geplaatst niet kan omzeilen en er een time-out optreedt. Hetzelfde resultaat zou optreden als de REPEATABLE READ- en SERIALIZABLE-isolatieniveaus werden gebruikt, omdat deze isolatieniveaus ook niet voorbij de vergrendelingen in de eerste transactie kunnen lezen.
Er wordt een vierde verbinding geopend en er wordt een transactie gestart met behulp van het isolatieniveau READ UNCOMMITTED, dat een vuile leesbewerking uitvoert van de niet-doorgevoerde waarde in sqlTransaction1. Deze waarde bestaat mogelijk nooit in de database als de eerste transactie niet is doorgevoerd.
De eerste transactie wordt teruggedraaid en opgeschoond door de TestSnapshot-tabel te verwijderen en de isolatie van momentopnamen voor de AdventureWorks-database uit te schakelen.
Opmerking
In de volgende voorbeelden wordt dezelfde verbindingsreeks gebruikt waarbij groepsgewijze verbindingen zijn uitgeschakeld. Als een verbinding is gegroepeerd, wordt bij het opnieuw instellen van het isolatieniveau het isolatieniveau op de server niet opnieuw ingesteld. Als gevolg hiervan beginnen volgende verbindingen die gebruikmaken van dezelfde gegroepeerde binnenverbinding met hun isolatieniveaus die zijn ingesteld op die van de gegroepeerde verbinding. Een alternatief voor het uitschakelen van groepsgewijze verbindingen is het expliciet instellen van het isolatieniveau voor elke verbinding.
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";
}
}
Example
In het volgende voorbeeld ziet u het gedrag van isolatie van momentopnamen wanneer gegevens worden gewijzigd. De code voert de volgende acties uit:
Maakt verbinding met de AdventureWorks-voorbeelddatabase en maakt momentopname-isolatie mogelijk.
Hiermee maakt u een tabel met de naam TestSnapshotUpdate en voegt u drie rijen met voorbeeldgegevens in.
Begint, maar voltooit niet, sqlTransaction1 met behulp van SNAPSHOT-isolatie. Er worden drie rijen met gegevens geselecteerd in de transactie.
Hiermee maakt u een tweede SqlConnection met AdventureWorks en maakt u een tweede transactie met behulp van het isolatieniveau READ COMMITTED waarmee een waarde wordt bijgewerkt in een van de rijen die zijn geselecteerd in sqlTransaction1.
Hiermee wordt sqlTransaction2 doorgevoerd.
Keert terug naar sqlTransaction1 en probeert dezelfde rij bij te werken die sqlTransaction1 al heeft doorgevoerd. Fout 3960 treedt op en sqlTransaction1 wordt automatisch teruggedraaid. De SqlException.Number en SqlException.Message worden weergegeven in het consolevenster.
Hiermee wordt opschooncode uitgevoerd om isolatie van momentopnamen in AdventureWorks uit te schakelen en de tabel TestSnapshotUpdate te verwijderen.
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"; } }
Vergrendelingshints gebruiken met isolatie van momentopnamen
In het vorige voorbeeld selecteert de eerste transactie gegevens en een tweede transactie werkt de gegevens bij voordat de eerste transactie kan worden voltooid, waardoor een updateconflict ontstaat wanneer de eerste transactie probeert dezelfde rij bij te werken. U kunt de kans op updateconflicten in langlopende momentopnametransacties verminderen door vergrendelingshints aan het begin van de transactie op te geven. In de volgende SELECT-instructie wordt de UPDLOCK-hint gebruikt om de geselecteerde rijen te vergrendelen:
SELECT * FROM TestSnapshotUpdate WITH (UPDLOCK)
WHERE PriKey BETWEEN 1 AND 3
Als u de UPDLOCK-vergrendeling hint gebruikt, worden de rijen die proberen bij te werken geblokkeerd totdat de eerste transactie is voltooid. Dit garandeert dat de geselecteerde rijen geen conflicten hebben wanneer ze later in de transactie worden bijgewerkt. Zie "Locking Hints" in SQL Server Books Online.
Als uw toepassing veel conflicten heeft, is isolatie van momentopnamen mogelijk niet de beste keuze. Hints moeten alleen worden gebruikt wanneer ze echt nodig zijn. Uw toepassing moet niet zo worden ontworpen dat deze voortdurend afhankelijk is van vergrendelingshints voor de werking ervan.