SQL Server'da anlık görüntü yalıtımı

ADO.NET indirin

Anlık görüntü yalıtımı, OLTP uygulamaları için eşzamanlılığı artırır.

Anlık görüntü izolasyonunu ve satır sürümlemeyi anlama

Anlık görüntü yalıtımı etkinleştirildikten sonra, her işlem için güncelleştirilmiş satır sürümleri tempdb'de tutulur. Benzersiz bir işlem sırası numarası her işlemi tanımlar ve bu benzersiz sayılar her satır sürümü için kaydedilir. İşlem, işlemin sıra numarasından önceki sıra numarasına sahip en güncel satır sürümleriyle çalışır. İşlem başlatıldıktan sonra oluşturulan yeni satır sürümleri işlem tarafından yoksayılır.

"Anlık görüntü" terimi, işlemdeki tüm sorguların, işlemin başladığı anda veritabanının durumuna bağlı olarak veritabanının aynı sürümünü veya anlık görüntüsünü gördüğü gerçeğini yansıtır. Anlık görüntü işleminde temel alınan veri satırlarına veya veri sayfalarına kilit alınmaz ve bu da önceki tamamlanmamış bir işlem tarafından engellenmeden diğer işlemlerin yürütülmesine izin verir. Verileri değiştiren işlemler, verileri okuyan işlemleri engellemez ve verileri okuyan işlemler, normalde SQL Server'da varsayılan READ COMMITTED yalıtım düzeyi altında olduğu gibi veri yazan işlemleri engellemez. Bu engelleyici olmayan davranış, karmaşık işlemler için kilitlenme olasılığını da önemli ölçüde azaltır.

Anlık görüntü yalıtımı iyimser bir eşzamanlılık modeli kullanır. Bir anlık görüntü işlemi, işlem başladıktan sonra değişen verilerde değişiklikler gerçekleştirmeye çalışırsa, işlem geri alınır ve bir hata oluşur. Değiştirilecek verilere erişen SELECT deyimleri için UPDLOCK ipuçlarını kullanarak bunu önleyebilirsiniz. Daha fazla bilgi için SQL Server Books Online'da "Kilitleme İpuçları" bölümüne bakın.

Anlık görüntü yalıtımı, işlemlerde kullanılmadan önce ALLOW_SNAPSHOT_ISOLATION ON veritabanı seçeneği ayarlanarak etkinleştirilmelidir. Bu, satır sürümlerini geçici veritabanında (tempdb) depolama mekanizmasını etkinleştirir. bunu Transact-SQL ALTER DATABASE deyimiyle kullanan her veritabanında anlık görüntü yalıtımını etkinleştirmeniz gerekir. Bu açıdan anlık görüntü yalıtımı, yapılandırma gerektirmeyen READ COMMITTED, REPEATABLE READ, SERIALIZABLE ve READ UNCOMMITTED gibi geleneksel yalıtım düzeylerinden farklıdır. Aşağıdaki deyimler anlık görüntü yalıtımını etkinleştirir ve varsayılan READ COMMITTED davranışını SNAPSHOT ile değiştirir:

ALTER DATABASE MyDatabase  
SET ALLOW_SNAPSHOT_ISOLATION ON  
  
ALTER DATABASE MyDatabase  
SET READ_COMMITTED_SNAPSHOT ON  

READ_COMMITTED_SNAPSHOT ON seçeneğinin ayarlanması, varsayılan READ COMMITTED yalıtım düzeyi altındaki sürümlenmiş satırlara erişim sağlar. READ_COMMITTED_SNAPSHOT seçeneği KAPALI olarak ayarlandıysa, sürümlenmiş satırlara erişmek için her oturum için Anlık Görüntü yalıtım düzeyini açıkça ayarlamanız gerekir.

İzolasyon düzeyleriyle eşzamanlılığı yönetme

Bir Transact-SQL deyiminin yürütüleceği yalıtım düzeyi, kilitleme ve satır sürümleme davranışını belirler. Yalıtım düzeyi, bağlantı genelinde kapsama sahiptir ve SET TRANSACTION ISOLATION LEVEL deyimiyle bir bağlantı için ayarlandıktan sonra, bağlantı kapatılana veya başka bir yalıtım düzeyi ayarlanana kadar etkin kalır. Bir bağlantı kapatılıp havuza döndürüldüğünde, son SET TRANSACTION ISOLATION LEVEL ifadesi tarafından belirlenen yalıtım düzeyi korunur. Havuza alınan bağlantıyı yeniden kullanan sonraki bağlantılar, bağlantı havuza alındığı sırada geçerli olan yalıtım düzeyini kullanır.

Bir bağlantı içinde verilen tek tek sorgular, tek bir deyim veya işlem için yalıtımı değiştiren ancak bağlantının yalıtım düzeyini etkilemeyen kilit ipuçları içerebilir. Saklı yordamlarda veya işlevlerde ayarlanan yalıtım düzeyleri veya kilit ipuçları, onları çağıran bağlantının yalıtım düzeyini değiştirmez ve yalnızca saklı yordam veya işlev çağrısı süresi boyunca etkindir.

SQL-92 standardında tanımlanan dört yalıtım düzeyi, SQL Server'ın ilk sürümlerinde destekleniyordu:

  • READ UNCOMMITTED, diğer işlemler tarafından yerleştirilen kilitleri yoksaydığından en az kısıtlayıcı yalıtım düzeyidir. READ UNCOMMITTED altında yürütülen işlemler, diğer işlemler tarafından henüz işlenmemiş değiştirilmiş veri değerlerini okuyabilir; bunlara "kirli" okuma denir.

  • READ COMMITTED, SQL Server için varsayılan yalıtım düzeyidir. İfadelerin, değiştirilmiş ancak henüz diğer işlemler tarafından onaylanmamış veri değerlerini okuyamayacağını belirterek kirli okumaları önler. Diğer işlemler yine de mevcut işlem sırasında tek tek ifadelerin yürütülmesi arasında verileri değiştirebilir, ekleyebilir veya silebilir ve bu da yinelenemeyen okumalar veya "hayalet" verilerle sonuçlanabilir.

  • REPEATABLE READ, READ COMMITTED değerinden daha kısıtlayıcı bir yalıtım düzeyidir. READ COMMITTED'ı kapsar ve ayrıca geçerli işlem tamamlanana kadar, geçerli işlem tarafından okunan verilerin başka hiçbir işlem tarafından değiştirilemeyeceğini veya silinemeyeceğini belirtir. Okunan verilerdeki paylaşılan kilitler her deyimin sonunda yayımlanmak yerine işlem süresi boyunca tutulacağından eşzamanlılık READ COMMITTED değerinden daha düşüktür.

  • SERIALIZABLE en kısıtlayıcı yalıtım düzeyidir çünkü tüm anahtar aralıklarını kilitler ve işlem tamamlanana kadar kilitleri tutar. REPEATABLE READ'i kapsar ve işlem tamamlanana kadar diğer işlemlerin işlem tarafından okunan aralıklara yeni satır ekleyememesi kısıtlamasını ekler.

Daha fazla bilgi için İşlem Kilitleme ve Satır Sürüm Oluşturma Kılavuzu'na bakın.

Anlık görüntü yalıtım düzeyi uzantıları

SQL Server, SNAPSHOT yalıtım düzeyinin kullanıma sunulması ve READ COMMITTED uygulamasının ek uygulanmasıyla SQL-92 yalıtım düzeylerine uzantılar getirmektedir. READ_COMMITTED_SNAPSHOT yalıtım düzeyi, tüm işlemler için READ COMMITTED değerini saydam bir şekilde değiştirebilir.

  • SNAPSHOT yalıtımı, bir işlem içinde okunan verilerin hiçbir zaman diğer eşzamanlı işlemler tarafından yapılan değişiklikleri yansıtmayacağını belirtir. İşlem, işlem başladığında var olan veri satırı sürümlerini kullanır. Veriler okunduğunda verilere kilit uygulanmaz, bu nedenle anlık görüntü işlemleri diğer işlemlerin veri yazmasını engellemez. Veri yazan işlemler, anlık görüntü işlemlerinin verileri okumalarını engellemez. Kullanmak için ALLOW_SNAPSHOT_ISOLATION veritabanı seçeneğini ayarlayarak anlık görüntü yalıtımını etkinleştirmeniz gerekir.

  • READ_COMMITTED_SNAPSHOT veritabanı seçeneği, veritabanında anlık görüntü yalıtımı etkinleştirildiğinde varsayılan READ COMMITTED yalıtım düzeyinin davranışını belirler. READ_COMMITTED_SNAPSHOT ON'u açıkça belirtmezseniz, READ COMMITTED tüm örtük işlemlere uygulanır. Bu, READ_COMMITTED_SNAPSHOT OFF (varsayılan) ayarıyla aynı davranışı üretir. READ_COMMITTED_SNAPSHOT KAPALI olduğunda, Veritabanı Motoru varsayılan yalıtım düzeyini sağlamak için paylaşılan kilitleri kullanır. READ_COMMITTED_SNAPSHOT veritabanı seçeneğini ON olarak ayarlarsanız, veritabanı altyapısı verileri korumak için kilitleri kullanmak yerine varsayılan olarak satır sürümü oluşturma ve anlık görüntü yalıtımı kullanır.

Anlık görüntü yalıtımı ve satır sürümleme nasıl çalışır?

SNAPSHOT yalıtım düzeyi etkinleştirildiğinde, bir satır her güncelleştirildiğinde, SQL Server Veritabanı Altyapısı özgün satırın bir kopyasını tempdb'de depolar ve satıra bir işlem dizisi numarası ekler. Gerçekleşen olayların sırası aşağıdadır:

  1. Yeni bir işlem başlatılır ve bir işlem sırası numarası atanır.

  2. Veritabanı Altyapısı, işlem içindeki bir satırı okur ve sıra numarası işlem dizisi numarasına en yakın ve daha düşük olan tempdb'den satır sürümünü alır.

  3. Veritabanı Motoru, anlık görüntü işlemi başlatıldığında işlem sıra numarasının, kaydedilmemiş işlemlerin etkin işlem dizisi numaraları listesinde olmadığını denetler.

  4. İşlem, işlemin başlangıcındaki mevcut tempdb'deki satırın sürümünü okur. İşlem başlatıldıktan sonra eklenen yeni satırları görmez çünkü bu sıra numarası değerleri işlem dizisi numarasının değerinden daha yüksek olacaktır.

  5. Geçerli işlem, işlem başladıktan sonra silinen satırları görür çünkü tempdb'de daha düşük bir sıra numarası değerine sahip bir satır sürümü olacaktır.

Anlık görüntü yalıtımının net etkisi, işlemin temel alınan tablolara herhangi bir kilit eklemeden, işlemin başlangıcında mevcut olan tüm verileri görmesidir. Bu, çekişme olduğu durumlarda performans geliştirmelerine neden olabilir.

Anlık görüntü işlemi her zaman iyimser eşzamanlılık denetimi kullanır ve diğer işlemlerin satırları güncellemesini engelleyecek kilitleri kullanmaktan kaçınır. Anlık görüntü işlemi, işlem başladıktan sonra değiştirilen bir satıra güncelleştirme işlemeye çalışırsa, işlem geri alınır ve bir hata oluşur.

ADO.NET anlık görüntü yalıtımı ile çalışma

ADO.NET'te anlık görüntü yalıtımı, SqlTransaction sınıfı tarafından desteklenir. Veritabanı anlık görüntü yalıtımı için etkinleştirildiyse ancak READ_COMMITTED_SNAPSHOT ON için yapılandırılmadıysa, yöntemini çağırırken SqlTransactionIsolationLevel.Snapshot numaralandırma değerini kullanarak bir BeginTransaction başlatmanız gerekir. Bu kod parçası, bağlantının açık SqlConnection bir nesne olduğunu varsayar.

SqlTransaction sqlTran =   
  connection.BeginTransaction(IsolationLevel.Snapshot);  

Example

Aşağıdaki örnek, kilitli verilere erişmeye çalışarak farklı yalıtım düzeylerinin nasıl davrandığını gösterir ve üretim kodunda kullanılması amaçlanmamıştır.

Kod, SQL Server'daki AdventureWorks örnek veritabanına bağlanır ve TestSnapshot adlı bir tablo oluşturur ve bir veri satırı ekler. Kod, veritabanı için anlık görüntü yalıtımını açmak için ALTER DATABASE Transact-SQL deyimini kullanır, ancak varsayılan READ COMMITTED yalıtım düzeyi davranışını etkin bırakarak READ_COMMITTED_SNAPSHOT seçeneğini ayarlamaz. Kod daha sonra aşağıdaki eylemleri gerçekleştirir:

  1. Güncelleştirme işlemini başlatmak için SERIALIZABLE yalıtım düzeyini kullanan sqlTransaction1 başlar ancak tamamlanmaz. Bu, tabloyu kilitleme etkisine sahiptir.

  2. İkinci bir bağlantı açar ve TestSnapshot tablosundaki verileri okumak için SNAPSHOT yalıtım düzeyini kullanarak ikinci bir işlem başlatır. Anlık görüntü yalıtımı etkinleştirildiğinden, bu işlem sqlTransaction1 başlatılmadan önce var olan verileri okuyabilir.

  3. Üçüncü bir bağlantı açar ve tablodaki verileri okumaya çalışmak için READ COMMITTED yalıtım düzeyini kullanarak bir işlem başlatır. ** Bu durumda kod, ilk işlemde tabloya yerleştirilen kilitleri okuyamadığından verileri okuyamaz ve zaman aşımına uğrar. REPEATABLE READ ve SERIALIZABLE yalıtım düzeyleri kullanılırsa, bu yalıtım düzeyleri de ilk işlemde yerleştirilen kilitleri okuyamadığından aynı sonuç ortaya çıkar.

  4. Dördüncü bir bağlantı açar ve SQLTransaction1'deki kaydedilmemiş değerin kirli bir okumasını gerçekleştiren READ UNCOMMITTED yalıtım düzeyini kullanarak bir işlem başlatır. İlk işlem işlenmediyse bu değer veritabanında hiçbir zaman mevcut olmayabilir.

  5. İlk işlemi geri alır ve TestSnapshot tablosunu silerek ve AdventureWorks veritabanı için anlık görüntü yalıtımını kapatarak temizler.

Uyarı

Aşağıdaki örneklerde, bağlantı havuzu kapalıyken aynı bağlantı dizesi kullanılır. Bir bağlantı havuza alındıysa, yalıtım düzeyi sıfırlandığında sunucudaki yalıtım düzeyi de sıfırlanmaz. Sonuç olarak, aynı havuzdaki iç bağlantıyı kullanan sonraki bağlantılar, izolasyon seviyeleri havuz bağlantısının seviyesine ayarlanmış olarak başlar. Bağlantı havuzunu kapatmanın bir alternatifi, her bağlantı için yalıtım düzeyini açıkça ayarlamaktır.

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

Aşağıdaki örnek, veriler değiştirilirken anlık görüntü yalıtımının davranışını gösterir. Kod aşağıdaki eylemleri gerçekleştirir:

  1. AdventureWorks örnek veritabanına bağlanır ve SNAPSHOT yalıtımını etkinleştirir.

  2. TestSnapshotUpdate adlı bir tablo oluşturur ve üç örnek veri satırı ekler.

  3. SNAPSHOT yalıtımı kullanarak sqlTransaction1 başlatılır, ancak tamamlanmaz. İşlemde üç veri satırı seçilir.

  4. AdventureWorks'e ikinci bir SqlConnection oluşturur ve SQLTransaction1'de seçilen satırlardan birinde bir değeri güncelleştiren READ COMMITTED yalıtım düzeyini kullanarak ikinci bir işlem oluşturur.

  5. commits sqlTransaction2.

  6. sqlTransaction1'e döner ve sqlTransaction1'in zaten işlediği satırı güncelleştirmeye çalışır. Hata 3960 oluştu ve sqlTransaction1 otomatik olarak geri alındı. SqlException.Number ve SqlException.Message, Konsol penceresinde görüntülenir.

  7. AdventureWorks'te anlık görüntü yalıtımını kapatmak ve TestSnapshotUpdate tablosunu silmek için temizleme kodunu yürütür.

    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";
        }
    
    }
    

Anlık görüntü yalıtımı ile kilit ipuçlarını kullanma

Önceki örnekte, ilk işlem verileri seçer ve ikinci bir işlem ilk işlem tamamlanmadan önce verileri güncelleştirir ve ilk işlem aynı satırı güncelleştirmeye çalıştığında güncelleştirme çakışmasına neden olur. İşlemin başında kilit ipuçları sağlayarak uzun süre çalışan anlık görüntü işlemlerinde güncelleştirme çakışmaları olasılığını azaltabilirsiniz. Aşağıdaki SELECT deyimi, seçili satırları kilitlemek için UPDLOCK ipucunu kullanır:

SELECT * FROM TestSnapshotUpdate WITH (UPDLOCK)   
  WHERE PriKey BETWEEN 1 AND 3  

UPDLOCK kilit ipucunu kullanmak, ilk işlem tamamlanmadan önce satırları güncelleştirmeye çalışan satırları engeller. Bu, seçilen satırların işlemde daha sonra güncelleştirildiğinde çakışma olmamasını garanti eder. Bkz. SQL Server Books Online'daki "Kilit İpuçları".

Uygulamanızın birçok çakışması varsa, anlık görüntü yalıtımı en iyi seçenek olmayabilir. İpuçları yalnızca gerçekten gerektiğinde kullanılmalıdır. Uygulamanız, işlemi için sürekli kilit ipuçlarına dayalı olacak şekilde tasarlanmamalıdır.

Sonraki Adımlar