使用快照隔离snapshot isolation实例(一)

根据微软《使用快照隔离》的内容,运行了其中的例子并给出中文注释。

示例

以下示例通过尝试访问锁定的数据,演示不同隔离级别的行为,并非要在生产代码中使用。

该代码连接到 SQL Server 中的 AdventureWorks 示例数据库上,并创建一个名为 TestSnapshot 的表,然后插入一行数据。该代码使用 ALTER DATABASE Transact-SQL 语句对数据库启用快照隔离,但是不设置 READ_COMMITTED_SNAPSHOT 选项,让默认的 READ COMMITTED 隔离级别的行为生效。然后,该代码执行下列操作:

  • 开始但是不完成 sqlTransaction1,sqlTransaction1 使用 SERIALIZABLE 隔离级别开始更新事务。这样做的结果是锁定表。

  • 打开第二个连接,并使用 SNAPSHOT 隔离级别开始第二个事务,读取 TestSnapshot 表中的数据。因为启用了快照隔离,此事务可以读取在开始 sqlTransaction1 之前存在的数据。

  • 打开第三个连接,并使用 READ COMMITTED 隔离级别开始一个事务,尝试读取表中的数据。在这种情况下,代码无法读取数据,因为代码在第一个事务中无法通过在表上放置的锁进行读取,因而超时。如果使用 REPEATABLE READ 和 SERIALIZABLE 隔离级别,因为这些隔离级别也无法通过第一个事务中放置的锁,因而会出现同样的结果。

  • 打开第四个连接,并使用 READ UNCOMMITTED 隔离级别开始一个事务,对 sqlTransaction1 中未提交的值执行脏读。如果第一个事务未提交,数据库中永远不会真正存在此值。

  • 回滚第一个事务,并通过删除 TestSnapshot 表以及禁用 AdventureWorks 数据库的快照隔离来进行清理。

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
namespace Snapshot
{
    class Program
    {
        static void Main(string[] args)
        {
            /*在字符串前加一个“@”字符的意思是取消字符中转意字符的功能。
            string s1 = @"a//b";
            string s2 = "a//b";
            Console.WriteLine(s1+"|"+s2);
            */
            
            /*测试数据库连接
            string connectionString = @"Data Source=XUWEI/SQLEXPRESS;Initial Catalog=demo;User ID=sa;Password=sa12345";
            SqlConnection conn = new SqlConnection(connectionString);
            conn.Open();
            SqlCommand cmd = conn.CreateCommand();
            cmd.CommandText = "select * from test";
            SqlDataReader reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                Console.WriteLine(reader.GetValue(0).ToString()
                    + "," + reader.GetValue(1).ToString());
            }
            */
            //定义数据库连接字符串。
            string connectionString = @"Data Source=XUWEI/SQLEXPRESS;Initial Catalog=AdventureWorks;User ID=sa;Password=sa12345";
            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,设置数据库使其支持快照隔离(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.
                /*开始,但是并不完成实物,一个事务在Serializable隔离级别上更新数据,
                 * 这样就对这张表加了锁,知道事务提交或者回滚为止。原始数据是(1,1),现在将变成(1,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.
                    /*初始化第二个事务,在快照隔离级别上读取表中的数据,这样读到的数据将是原始的数据,
                     * 因为事务1还没有提交。
                     */
                    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());
                    }
                    reader2.Close();
                    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.
                /*打开第三个数据库连接,创建一个在ReadCommitted隔离级别上读取表的事务。
                 * 这个事务不能读取表中的数据,这是因为事务1已经对表加了锁。数据访问将在4妙后超时。
                 * 在RepeatableRead 和 Serializable 隔离级别上也同样不能读取表中的数据。                
                 */
                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.");
                        }
                        sqldatareader3.Close();
                        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.
                /*打开第四个数据库连接,创建一个在ReadUncommitted隔离级别上读取表的事务。
                 * ReadUncommitted不会触碰到表中的锁,而且能够“脏读”到被修改但是没有被提交的数据22。
                 * 如果事务1回滚了,数据库中将不会存在22这个值。
                 */
                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());
                    }
                    reader4.Close();
                    transaction4.Commit();
                }
                // Roll back the first transaction,回滚事务1。
                transaction1.Rollback();
            }
            // CLEANUP
            // Delete the TestSnapshot table and set
            // ALLOW_SNAPSHOT_ISOLATION OFF
            /*清理
             * 删除TestSnapshot表和集合
             * 置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!");
            
        }
    }
}
作者:xwdreamer
欢迎任何形式的转载,但请务必注明出处。
分享到:
原文地址:https://www.cnblogs.com/xwdreamer/p/2297080.html