Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Каква е причината контекстът на транзакция да се използва от друга сесия

Малко е късно за отговор :), но се надявам, че ще бъде полезен за други. Отговорът се състои от три части:

  1. Какво означава „Контекстът на транзакцията се използва от друга сесия.“
  2. Как да възпроизведете грешка „Контекстът на транзакцията се използва от друга сесия.“

1. Какво означава „Контекстът на транзакцията се използва от друга сесия.“

Важна забележка:Заключването на контекста на транзакцията се придобива точно преди и се освобождава веднага след взаимодействието между SqlConnections и SQL Server.

Когато изпълните някаква SQL заявка, SqlConnections "изглежда" има ли транзакция, която го обвива. Може да е SqlTransaction ("роден" за SqlConnection) или Transaction от System.Transactions монтаж.

При намерена транзакция SqlConnections използва го за комуникация със SQL Server и в момента те комуникират Transaction контекстът е изключително заключен.

Какво означава TransactionScope ? Той създава Transaction и предоставя информация за компонентите на .NET Framework за него, така че всеки, включително SqlConnection, може (и по замисъл трябва) да го използва.

И така, деклариране на TransactionScope ние създаваме нова транзакция, която е достъпна за всички обекти с възможност за транзакции, създадени в текущата Thread .

Описаната грешка означава следното:

  1. Създадохме няколко SqlConnections под същия TransactionContext (което означава, че са свързани с една и съща транзакция)
  2. Попитахме тези SqlConnections за комуникация със SQL Server едновременно
  3. Един от тях заключи текущата Transaction контекст и следващата изведена грешка

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

И възпроизведете „Контекст на транзакция, използван от друга сесия.“ грешка с C# код, базиран на примерен код на Shrike

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 / и т.н.... заявки в една опашка и ги обслужвайте с еднонишков работник;
  • В многонишкови приложения използвайте транзакции. Винаги. навсякъде. Дори за четене;
  • Не споделяйте една транзакция между множество нишки. Причинява странно, неочевидно, трансцендентално и невъзпроизводимо съобщения за грешка:
    • „Контекст на транзакция, използван от друга сесия.“:множество едновременни взаимодействия със сървъра при една транзакция;
    • „Времето е изтекло. Периодът на изчакване е изтекъл преди завършване на операцията или сървърът не отговаря.“:независещите транзакции са завършени;
    • „Транзакцията е под съмнение.“;
    • ... и предполагам много други ...
  • Не забравяйте да зададете ниво на изолация за TransactionScope . По подразбиране е Serializable но в повечето случаи ReadCommitted е достатъчно;
  • Не забравяйте да Complete() TransactionScope и DependentTransaction


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. .NET API за SQL Server Service Broker

  2. Как да дезинфекцирам входа с PHP и sqlsrv драйвера?

  3. Събития на изчакване на SQL сървър -2

  4. Промяна на потребителски дефиниран тип в SQL Server

  5. Възможно ли е да се изпълни текстов файл от SQL заявка?