sql >> データベース >  >> RDS >> Sqlserver

別のセッションでトランザクションコンテキストが使用されている理由は何ですか

    回答には少し遅れていますが、他の人にも役立つことを願っています。回答には3つの部分があります:

    1. 「別のセッションで使用されているトランザクションコンテキスト」とはどういう意味ですか。
    2. エラー「別のセッションで使用されているトランザクションコンテキスト」を再現する方法

    1。 「別のセッションで使用されているトランザクションコンテキスト」とはどういう意味ですか。

    重要なお知らせ:トランザクションコンテキストロックは、SqlConnection間の相互作用の直前に取得され、直後に解放されます。 およびSQLServer。

    SQLクエリを実行するときは、SqlConnection 「見た目」は、それをラップするトランザクションがあります。 SqlTransactionの可能性があります (SqlConnectionの場合は「ネイティブ」)またはTransaction System.Transactionsから 組み立て。

    トランザクションが見つかったときSqlConnection これを使用してSQLServerと通信し、現時点ではTransactionと通信します。 コンテキストは排他的にロックされています。

    TransactionScopeとは ? Transactionを作成します .NET Frameworkコンポーネントに関する情報を提供するため、SqlConnectionを含むすべての人がそれを使用できます(設計上は使用する必要があります)。

    したがって、TransactionScopeを宣言します 現在のThreadでインスタンス化されたすべての「トランザクション可能」オブジェクトで使用できる新しいトランザクションを作成しています 。

    説明されているエラーとは、次のことを意味します。

    1. いくつかのSqlConnectionsを作成しました 同じTransactionContextの下で (つまり、同じトランザクションに関連している)
    2. これらのSqlConnectionに質問しました SQLServerと同時に通信する
    3. そのうちの1つが現在のTransactionをロックしました コンテキストと次の1つがエラーをスローしました

    2。エラー「別のセッションで使用されているトランザクションコンテキスト」を再現する方法

    まず、SQLコマンドの実行時にトランザクションコンテキストが使用(「ロック」)されます。したがって、そのような動作を確実に再現することは困難です。

    ただし、単一のトランザクションで比較的長いSQL操作を実行する複数のスレッドを開始することでそれを試みることができます。テーブル[dbo].[Persons]を準備しましょう。 [tests]で データベース:

    USE [tests]
    GO
    DROP TABLE [dbo].[Persons]
    GO
    CREATE TABLE [dbo].[Persons](
        [Id] [bigint] IDENTITY(1,1) NOT NULL PRIMARY KEY,
        [Name] [nvarchar](1024) NOT NULL,
        [Nick] [nvarchar](1024) NOT NULL,
        [Email] [nvarchar](1024) NOT NULL)
    GO
    DECLARE @Counter INT
    SET @Counter = 500
    
    WHILE (@Counter > 0) BEGIN
        INSERT [dbo].[Persons] ([Name], [Nick], [Email])
        VALUES ('Sheev Palpatine', 'DarthSidious', '[email protected]')
        SET @Counter = @Counter - 1
    END
    GO
    

    そして、「別のセッションで使用されているトランザクションコンテキスト」を再現します。 Shrikeコード例に基づくC#コードのエラー

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Transactions;
    using System.Data.SqlClient;
    
    namespace SO.SQL.Transactions
    {
        public static class TxContextInUseRepro
        {
            const int Iterations = 100;
            const int ThreadCount = 10;
            const int MaxThreadSleep = 50;
            const string ConnectionString = "Initial Catalog=tests;Data Source=.;" +
                                            "User ID=testUser;PWD=Qwerty12;";
            static readonly Random Rnd = new Random();
            public static void Main()
            {
                var txOptions = new TransactionOptions();
                txOptions.IsolationLevel = IsolationLevel.ReadCommitted;
                using (var ctx = new TransactionScope(
                    TransactionScopeOption.Required, txOptions))
                {
                    var current = Transaction.Current;
                    DependentTransaction dtx = current.DependentClone(
                        DependentCloneOption.BlockCommitUntilComplete);               
                    for (int i = 0; i < Iterations; i++)
                    {
                        // make the transaction distributed
                        using (SqlConnection con1 = new SqlConnection(ConnectionString))
                        using (SqlConnection con2 = new SqlConnection(ConnectionString))
                        {
                            con1.Open();
                            con2.Open();
                        }
    
                        var threads = new List<Thread>();
                        for (int j = 0; j < ThreadCount; j++)
                        {
                            Thread t1 = new Thread(o => WorkCallback(dtx));
                            threads.Add(t1);
                            t1.Start();
                        }
    
                        for (int j = 0; j < ThreadCount; j++)
                            threads[j].Join();
                    }
                    dtx.Complete();
                    ctx.Complete();
                }
            }
    
            private static void WorkCallback(DependentTransaction dtx)
            {
                using (var txScope1 = new TransactionScope(dtx))
                {
                    using (SqlConnection con2 = new SqlConnection(ConnectionString))
                    {
                        Thread.Sleep(Rnd.Next(MaxThreadSleep));
                        con2.Open();
                        using (var cmd = new SqlCommand("SELECT * FROM [dbo].[Persons]", con2))
                        using (cmd.ExecuteReader()) { } // simply recieve data
                    }
                    txScope1.Complete();
                }
            }
        }
    }
    

    そして結論として、アプリケーションにトランザクションサポートを実装することについてのいくつかの言葉:

    • 可能であれば、マルチスレッドデータ操作は避けてください(ロードまたは保存に関係なく)。例えば。 SELECTを保存します / UPDATE / etc ...単一のキューでリクエストし、シングルスレッドワーカーでそれらを処理します;
    • マルチスレッドアプリケーションでは、トランザクションを使用します。いつも。どこにでも。読むためにも;
    • 複数のスレッド間で単一のトランザクションを共有しないでください。それは奇妙で、自明ではなく、超越的で、再現性がない原因となります。 エラーメッセージ:
      • 「別のセッションで使用されているトランザクションコンテキスト。」:1つのトランザクションでサーバーとの複数の同時インタラクション。
      • 「タイムアウトが期限切れになりました。操作が完了する前にタイムアウト期間が経過したか、サーバーが応答していません。」:依存しないトランザクションが完了しました。
      • 「取引は疑わしいです。」;
      • ...そして私は他にもたくさんのことを想定しています...
    • TransactionScopeの分離レベルを設定することを忘れないでください 。デフォルトはSerializable ただし、ほとんどの場合、ReadCommitted 十分です;
    • Complete()TransactionScopeを忘れないでください およびDependentTransaction


    1. MySQLでテーブルを列名で並べ替える

    2. postgresql9.4または9.5のjsonオブジェクトのネストされた配列内の要素をクエリします

    3. PostgreSQLの例外処理

    4. CASEを使用せずにMySQLの行を列にピボットする方法はありますか?