sql >> Database teknologi >  >> RDS >> Sqlserver

Hvad er årsagen til transaktionskonteksten, der bruges af en anden session

Det er lidt sent at svare :) men håber det vil være nyttigt for andre. Svaret indeholder tre dele:

  1. Hvad betyder det "Transaktionskontekst i brug af en anden session."
  2. Sådan genskabes fejlen "Transaktionskontekst i brug af en anden session."

1. Hvad betyder det "Transaktionskontekst i brug af en anden session."

Vigtig bemærkning:Transaktionskontekstlås erhverves lige før og frigives umiddelbart efter interaktion mellem SqlConnection og SQL Server.

Når du udfører en SQL-forespørgsel, SqlConnection "ser" er der nogen transaktion, der pakker det ind. Det kan være SqlTransaction ("native" for SqlConnection) eller Transaction fra System.Transactions montage.

Når transaktionen blev fundet SqlConnection bruger det til at kommunikere med SQL Server og i øjeblikket kommunikerer de Transaction konteksten er udelukkende låst.

Hvad betyder TransactionScope ? Det opretter Transaction og giver .NET Framework Components-information om det, så alle inklusive SqlConnection kan (og skal designes) bruge det.

Så erklærer TransactionScope vi opretter en ny transaktion, som er tilgængelig for alle "transaktionerbare" objekter instansieret i den nuværende Thread .

Beskrevet fejl betyder følgende:

  1. Vi har oprettet flere SqlConnections under den samme TransactionContext (hvilket betyder, at de var relateret til den samme transaktion)
  2. Vi spurgte disse SqlConnection at kommunikere med SQL Server samtidigt
  3. En af dem låste den aktuelle Transaction kontekst og den næste gav fejl

2. Sådan genskabes fejlen "Transaktionskontekst i brug af en anden session."

Først og fremmest bruges transaktionskontekst ("låst") lige på tidspunktet for udførelse af sql-kommando. Så det er svært at gengive sådan en adfærd med sikkerhed.

Men vi kan prøve at gøre det ved at starte flere tråde, der kører relativt lange SQL-operationer under den enkelte transaktion. Lad os forberede tabellen [dbo].[Persons] i [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

Og genskab "Transaktionskontekst i brug af en anden session." fejl med C#-kode baseret på Shrike-kodeeksempel

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

Og afslutningsvis et par ord om implementering af transaktionssupport i din applikation:

  • Undgå multitrådede dataoperationer, hvis det er muligt (uanset indlæsning eller lagring). For eksempel. gem SELECT /UPDATE / etc... forespørgsler i en enkelt kø og server dem med en enkelt-trådsarbejder;
  • I flertrådede applikationer skal du bruge transaktioner. Altid. Overalt. Selv til læsning;
  • Del ikke en enkelt transaktion mellem flere tråde. Det forårsager mærkelige, uoplagte, transcendentale og ikke reproducerbare fejlmeddelelser:
    • "Transaktionskontekst i brug af en anden session.":flere samtidige interaktioner med serveren under én transaktion;
    • "Timeout er udløbet. Timeoutperioden er forløbet før fuldførelse af handlingen, eller serveren reagerer ikke.":ikke-afhængige transaktioner blev gennemført;
    • "Transaktionen er i tvivl.";
    • ... og jeg går ud fra en masse andre ...
  • Glem ikke at indstille isolationsniveau for TransactionScope . Standard er Serializable men i de fleste tilfælde ReadCommitted er nok;
  • Glem ikke at Complete() TransactionScope og DependentTransaction


  1. Oracles dato og klokkeslæt uden sommertid via JDBC

  2. COUNT() Funktion i MariaDB

  3. Problem med kompleks mysql-forespørgsel ved hjælp af forberedt erklæring

  4. Brug mysqldump til at sikkerhedskopiere MySQL eller MariaDB