it-swarm.asia

لماذا "بدء المعاملة" قبل أن يقوم "إدراج الاستعلام" بتأمين الجدول بأكمله؟

أنا أستخدم SQL Server 2005 Express.

في سيناريو ، أضفت Begin Transaction الأمر قبل عبارة INSERT مباشرة في إجراء مخزن. عندما قمت بتنفيذ هذا الإجراء المخزن ، قام بإغلاق الجدول بأكمله وأظهرت جميع الاتصالات المتزامنة عرضًا معلقًا حتى وقت انتهاء INSERT.

لماذا يتم تأمين الجدول بأكمله وكيف أتغلب على هذه المشكلة في SQL Server 2005 Express؟

تحرير

الاستعلام على النحو التالي:

INSERT INTO <table2> SELECT * FROM <table1> WHERE table1.workCompleted = 'NO'
11
RPK

قد تكون هذه الإجابة مفيدة للسؤال الأصلي ولكنها في المقام الأول لمعالجة المعلومات غير الدقيقة في المنشورات الأخرى. كما يسلط الضوء على قسم من الهراء في BOL.

وكما هو مذكور في إدراج الوثائق ، سيحصل على قفل حصري على الطاولة. الطريقة الوحيدة لإجراء التحديد على الجدول هي استخدام NOLOCK أو تعيين مستوى العزل للمعاملة.

يوضح القسم المرتبط ببول:

تكتسب عبارة INSERT دائمًا قفلًا حصريًا (X) على الجدول الذي تعدله ، وتحتفظ بذلك القفل حتى تكتمل المعاملة. مع قفل حصري (X) ، لا يمكن لأي معاملات أخرى تعديل البيانات ؛ يمكن أن تتم عمليات القراءة فقط باستخدام تلميح NOLOCK أو قراءة مستوى العزل غير الملزم. لمزيد من المعلومات ، راجع القفل في مشغل قاعدة البيانات .

ملحوظة: اعتبارًا من 2014-8-27 تم تحديث BOL لإزالة البيانات غير الصحيحة المذكورة أعلاه.

لحسن الحظ هذه ليست القضية إذا كانت هناك إدخالات إلى جدول ، فستحدث بشكل تسلسلي وسيتم حظر جميع القراء من الجدول بأكمله حتى تكتمل عملية الإدراج. وهذا سيجعل SQL Server خادم قاعدة بيانات فعال مثل NTFS. ليس كثيرًا.

يشير الفطرة السليمة إلى أنه لا يمكن أن يكون الأمر كذلك ولكن كما يشير بول راندال ، " قدم لنفسك معروفًا ، لا تثق بأحد ". إذا كنت لا تثق في أي شخص ، بما في ذلك BOL ، أعتقد أنه سيتعين علينا فقط إثبات ذلك.

قم بإنشاء قاعدة بيانات وملء جدول وهمي مع مجموعة من الصفوف ، مع ملاحظة إرجاع DatabaseId.

SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;

USE [master]
GO

IF EXISTS (SELECT name FROM sys.databases WHERE name = N'LockDemo')
DROP DATABASE [LockDemo]
GO

DECLARE @DataFilePath NVARCHAR(4000)
SELECT 
    @DataFilePath = SUBSTRING(physical_name, 1, CHARINDEX(N'master.mdf', LOWER(physical_name)) - 1)
FROM 
    master.sys.master_files
WHERE 
    database_id = 1 AND file_id = 1

EXEC ('
CREATE DATABASE [LockDemo] ON  PRIMARY 
( NAME = N''LockDemo'', FILENAME = N''' + @DataFilePath + N'LockDemo.mdf' + ''', SIZE = 2MB , MAXSIZE = UNLIMITED, FILEGROWTH = 2MB )
 LOG ON 
( NAME = N''LockDemo_log'', FILENAME = N''' + @DataFilePath + N'LockDemo_log.ldf' + ''', SIZE = 1MB , MAXSIZE = UNLIMITED , FILEGROWTH = 1MB )
')

GO

USE [LockDemo]
GO

SELECT DB_ID() AS DatabaseId

CREATE TABLE [dbo].[MyTable]
(
    [id] [int] IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , [filler] CHAR(4030) NOT NULL DEFAULT REPLICATE('A', 4030) 
)
GO

INSERT MyTable DEFAULT VALUES;
GO 100

قم بإعداد تتبع ملف التعريف الذي سيقوم بتتبع lock: المكتسبة والقفل: الأحداث التي تم إصدارها ، والتصفية في DatabaseId من البرنامج النصي السابق ، وتعيين مسار للملف وملاحظة TraceId الذي تم إرجاعه.

declare @rc int
declare @TraceID int
declare @maxfilesize BIGINT
declare @databaseid INT
DECLARE @tracefile NVARCHAR(4000)

set @maxfilesize = 5 
SET @tracefile = N'D:\Temp\LockTrace'
SET @databaseid = 9

exec @rc = sp_trace_create @TraceID output, 0, @tracefile, @maxfilesize, NULL 
if (@rc != 0) goto error

declare @on bit
set @on = 1
exec sp_trace_setevent @TraceID, 24, 32, @on
exec sp_trace_setevent @TraceID, 24, 1, @on
exec sp_trace_setevent @TraceID, 24, 57, @on
exec sp_trace_setevent @TraceID, 24, 3, @on
exec sp_trace_setevent @TraceID, 24, 51, @on
exec sp_trace_setevent @TraceID, 24, 12, @on
exec sp_trace_setevent @TraceID, 60, 32, @on
exec sp_trace_setevent @TraceID, 60, 57, @on
exec sp_trace_setevent @TraceID, 60, 3, @on
exec sp_trace_setevent @TraceID, 60, 51, @on
exec sp_trace_setevent @TraceID, 60, 12, @on
exec sp_trace_setevent @TraceID, 23, 32, @on
exec sp_trace_setevent @TraceID, 23, 1, @on
exec sp_trace_setevent @TraceID, 23, 57, @on
exec sp_trace_setevent @TraceID, 23, 3, @on
exec sp_trace_setevent @TraceID, 23, 51, @on
exec sp_trace_setevent @TraceID, 23, 12, @on

-- DatabaseId filter
exec sp_trace_setfilter @TraceID, 3, 0, 0, @databaseid

-- Set the trace status to start
exec sp_trace_setstatus @TraceID, 1

-- display trace id for future references
select [email protected]
goto finish

error: 
select [email protected]

finish: 
go

أدخل صفًا وأوقف التتبع:

USE LockDemo
GO
INSERT MyTable DEFAULT VALUES
GO
EXEC sp_trace_setstatus 3, 0
EXEC sp_trace_setstatus 3, 2
GO

افتح ملف التتبع وستجد ما يلي:

Profiler window

تسلسل الأقفال المأخوذة هو:

  1. القفل الحصري على MyTable
  2. القفل الحصري على الصفحة 1: 211
  3. RangeInsert-NullResource في إدخال فهرس متفاوت القيمة للقيمة التي يتم إدراجها
  4. قفل حصري على المفتاح

ثم يتم تحرير الأقفال بترتيب عكسي. في أي وقت لم يتم الحصول على قفل حصري على الطاولة.

ولكن هذه مجرد إدخال دفعة واحدة! هذا ليس مثل اثنين أو ثلاثة أو العشرات بالتوازي.

نعم إنه كذلك. SQL Server (ويمكن القول أنه أي محرك قاعدة بيانات علائقية) ليس لديه بصيرة فيما يتعلق بالدُفعات الأخرى التي يمكن تشغيلها عند معالجة عبارة و/أو مجموعة ، لذلك لا يختلف تسلسل الحصول على القفل.

ماذا عن مستويات عزل أعلى مثل قابل للتسلسل؟

لهذا المثال بالذات يتم أخذ نفس الأقفال بالضبط. لا تثق بي ، جربها!

25
Mark Storey-Smith

لا أقوم بالكثير من عمل T-SQL ولكن من قراءة الوثائق ...

هذا حسب التصميم كما هو موضح في BEGIN TRANSACTION :

اعتمادًا على إعدادات مستوى عزل المعاملة الحالية ، يتم تأمين العديد من الموارد المكتسبة لدعم عبارات SQL للعمليات الصادرة عن الاتصال بواسطة المعاملة حتى تكتمل مع عبارة COMMIT TRANSACTION أو ROLLBACK TRANSACTION.

وكما هو مذكور في إدراج الوثائق ، سيحصل على قفل حصري على الطاولة. الطريقة الوحيدة لإجراء التحديد على الجدول هي استخدام NOLOCK أو تعيين مستوى العزل للمعاملة.

0
user507