使用快照隔离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!");

            

        }

    }

}

 

 

你可能感兴趣的:(ISO)