• 使用快照隔离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
    欢迎任何形式的转载,但请务必注明出处。
    分享到:
  • 相关阅读:
    Codeforces Round #548
    省选前的th题
    省选前多项式的挣扎
    2019.3.18考试&2019.3.19考试&2019.3.21考试
    省选前的反演抢救计划
    2019.3.16 noiac的原题模拟赛
    AtCoder Regular Contest 069 F
    Atcoder Grand 012 C
    Atcoder Grand 011 C
    Atcoder Grand 006 C-Rabbit Exercise
  • 原文地址:https://www.cnblogs.com/xwdreamer/p/2297080.html
Copyright © 2020-2023  润新知