Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Lý do ngữ cảnh Giao dịch được sử dụng bởi một phiên khác là gì

Câu trả lời hơi muộn :) nhưng hy vọng nó sẽ hữu ích cho những người khác. Câu trả lời gồm ba phần:

  1. "Ngữ cảnh giao dịch được phiên khác sử dụng" có nghĩa là gì.
  2. Cách tạo lại lỗi "Bối cảnh giao dịch được một phiên sử dụng."

1. "Ngữ cảnh giao dịch được một phiên sử dụng" có nghĩa là gì.

Thông báo quan trọng:Khóa ngữ cảnh giao dịch được mua ngay trước đó và được giải phóng ngay sau khi tương tác giữa SqlConnection và SQL Server.

Khi bạn thực thi một số Truy vấn SQL, SqlConnection "có vẻ" là có bất kỳ giao dịch nào gói nó. Nó có thể là SqlTransaction ("gốc" cho SqlConnection) hoặc Giao dịch từ System.Transactions hội,, tổ hợp.

Khi giao dịch được tìm thấy SqlConnection sử dụng nó để giao tiếp với SQL Server và tại thời điểm này chúng giao tiếp Giao dịch ngữ cảnh bị khóa riêng.

TransactionScope có chức năng gì ? Nó tạo ra Giao dịch và cung cấp thông tin về Thành phần .NET Framework về nó, vì vậy tất cả mọi người bao gồm cả SqlConnection có thể (và theo thiết kế) nên sử dụng nó.

Vì vậy, khai báo TransactionScope chúng tôi đang tạo Giao dịch mới có sẵn cho tất cả các đối tượng "có thể giao dịch" được khởi tạo trong Luồng hiện tại .

Lỗi được mô tả có nghĩa như sau:

  1. Chúng tôi đã tạo một số SqlConnections trong cùng một TransactionContext (có nghĩa là chúng liên quan đến cùng một giao dịch)
  2. Chúng tôi đã hỏi những SqlConnection này để giao tiếp với SQL Server đồng thời
  3. Một trong số họ đã khóa Giao dịch hiện tại ngữ cảnh và một lỗi tiếp theo được ném ra

2. Cách tạo lại lỗi "Ngữ cảnh giao dịch được phiên khác sử dụng."

Trước hết, bối cảnh giao dịch được sử dụng ("bị khóa") ngay tại thời điểm thực hiện lệnh sql. Vì vậy, rất khó để tạo lại một hành vi như vậy chắc chắn.

Nhưng chúng ta có thể thử làm điều đó bằng cách bắt đầu nhiều luồng chạy các hoạt động SQL tương đối dài trong một giao dịch duy nhất. trong [tests] Cơ sở dữ liệu:

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

Và tái tạo "Ngữ cảnh giao dịch được sử dụng bởi một phiên khác." lỗi với mã C # dựa trên ví dụ về mã 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();
            }
        }
    }
}

Và kết luận một vài từ về việc triển khai hỗ trợ giao dịch trong ứng dụng của bạn:

  • Tránh các thao tác dữ liệu đa luồng nếu có thể (bất kể đang tải hay lưu). Ví dụ. lưu SELECT / CẬP NHẬT / etc ... yêu cầu trong một hàng đợi và phân phát chúng với một nhân viên đơn luồng;
  • Trong các ứng dụng đa luồng, sử dụng các giao dịch. Luôn luôn. Mọi nơi. Ngay cả để đọc;
  • Không chia sẻ giao dịch đơn lẻ giữa nhiều chuỗi. Nó gây ra sự kỳ lạ, không rõ ràng, siêu việt và không thể tái tạo thông báo lỗi:
    • "Bối cảnh giao dịch được một phiên sử dụng.":nhiều tương tác đồng thời với máy chủ trong một giao dịch;
    • "Hết thời gian chờ. Khoảng thời gian chờ đã trôi qua trước khi hoàn thành hoạt động hoặc máy chủ không phản hồi.":không phải giao dịch phụ thuộc đã được hoàn thành;
    • "Giao dịch đang bị nghi ngờ.";
    • ... và tôi giả định rất nhiều điều khác ...
  • Đừng quên đặt Mức cách ly cho TransactionScope . Mặc định là Serializable nhưng trong hầu hết các trường hợp, ReadComiled là đủ;
  • Đừng quên Hoàn thành () 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. Linq-to-SQL để chỉ tìm kiếm các phần DATE của một ngày

  2. Tại sao chuyển đổi từ DATETIME sang DATETIME2 dường như thay đổi giá trị?

  3. executive sp_executesql @sql và execute (@sql) SQL Server

  4. Khám phá các định dạng ngày Postgres và các chức năng khác nhau của chúng

  5. Giới thiệu Nền tảng SaaS đầu tiên trên thế giới để cung cấp chẩn đoán sâu cho môi trường máy chủ SQL kết hợp