sql >> Database >  >> RDS >> Sqlserver

Wat is de reden dat de transactiecontext door een andere sessie wordt gebruikt?

Het is een beetje laat voor antwoord :) maar ik hoop dat het nuttig zal zijn voor anderen.Antwoord bestaat uit drie delen:

  1. Wat betekent het "Transactiecontext in gebruik door een andere sessie."
  2. De fout 'Transactiecontext in gebruik door een andere sessie' reproduceren.

1. Wat betekent het "Transactiecontext in gebruik door een andere sessie."

Belangrijke opmerking:Transactiecontextvergrendeling wordt vlak voor verkregen en onmiddellijk vrijgegeven na interactie tussen SqlConnection en SQL-server.

Wanneer u een SQL-query uitvoert, SqlConnection "looks" is er een transactie die het inpakt. Het kan SqlTransaction zijn ("native" voor SqlConnection) of Transaction van System.Transactions bijeenkomst.

Wanneer transactie gevonden SqlConnection gebruikt het om te communiceren met SQL Server en op het moment dat ze communiceren Transaction context is exclusief vergrendeld.

Wat doet TransactionScope ? Het creëert Transaction en biedt informatie over .NET Framework Components erover, zodat iedereen, inclusief SqlConnection, het kan (en zou moeten) gebruiken.

Dus verklaren TransactionScope we creëren een nieuwe transactie die beschikbaar is voor alle "verhandelbare" objecten die zijn geïnstantieerd in de huidige Thread .

Beschreven fout betekent het volgende:

  1. We hebben verschillende SqlConnections gemaakt onder dezelfde TransactionContext (wat betekent dat ze betrekking hadden op dezelfde transactie)
  2. We vroegen deze SqlConnection gelijktijdig communiceren met SQL Server
  3. Een van hen heeft de huidige Transaction vergrendeld context en volgende fout gegooid

2. Hoe de fout "Transactiecontext in gebruik door een andere sessie" te reproduceren.

Allereerst wordt de transactiecontext gebruikt ("vergrendeld") op het moment dat de sql-opdracht wordt uitgevoerd. Het is dus moeilijk om dergelijk gedrag zeker te reproduceren.

Maar we kunnen het proberen door meerdere threads te starten die relatief lange SQL-bewerkingen uitvoeren onder de enkele transactie. Laten we de tabel [dbo].[Persons] voorbereiden. in [tests] Database:

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

En reproduceer 'Transactiecontext in gebruik door een andere sessie'. fout met C#-code op basis van Shrike-codevoorbeeld

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();
            }
        }
    }
}

En tot slot een paar woorden over het implementeren van transactieondersteuning in uw applicatie:

  • Vermijd multi-threaded databewerkingen als dat mogelijk is (ongeacht laden of opslaan). bijv. bewaar SELECT /UPDATE / etc... verzoeken in een enkele wachtrij en serveer ze met een single-thread worker;
  • Gebruik transacties in toepassingen met meerdere threads. Altijd. Overal. Zelfs om te lezen;
  • Deel geen enkele transactie tussen meerdere threads. Het veroorzaakt vreemde, onopvallende, transcendentale en niet reproduceerbare foutmeldingen:
    • "Transactiecontext in gebruik door een andere sessie.":meerdere gelijktijdige interacties met de server onder één transactie;
    • "Time-out verlopen. De time-outperiode is verstreken voordat de bewerking is voltooid of de server reageert niet.":niet-afhankelijke transacties zijn voltooid;
    • "De transactie is in twijfel.";
    • ... en ik neem aan dat veel andere ...
  • Vergeet niet het isolatieniveau in te stellen voor TransactionScope . Standaard is Serializable maar in de meeste gevallen ReadCommitted is genoeg;
  • Vergeet niet om TransactionScope te voltooien() en DependentTransaction


  1. Een beetje nieuwsgierig naar het `mysql -e` uitvoerformaat met &zonder omleiding

  2. Recursieve trigger voorkomen in PostgreSQL

  3. Hoe kan ik mijn query's samenvoegen tot een enkele query (of kan een opgeslagen proces zijn..)

  4. Hoe beïnvloedt {} een MySQL-query in PHP?