Tại sao Dịch vụ bắt đầu Giao dịch trực tiếp trước khi Truy vấn Chèn Truy cập khóa toàn bộ bảng?


11

Tôi đang sử dụng SQL Server 2005 Express.

Trong một kịch bản, tôi đã thêm Begin Transactionlệnh ngay trước một INSERTcâu lệnh trong một thủ tục được lưu trữ. Khi tôi thực hiện quy trình được lưu trữ này, nó đã khóa toàn bộ bảng và tất cả các kết nối đồng thời hiển thị màn hình treo cho đến khi INSERTkết thúc.

Tại sao toàn bộ bảng bị khóa và làm cách nào để khắc phục vấn đề này trong SQL Server 2005 Express?

Đã chỉnh sửa

Truy vấn như sau:

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

2
Nó không khóa bảng trong postgresql.
Scott Marlowe

Cần thêm @RPK. Với bảng DDL và một mẫu các phần chèn, chúng tôi có thể cung cấp cho bạn một lời giải thích chính xác về những gì đang xảy ra. Không có nó, chúng tôi chỉ đoán.
Mark Storey-Smith

Câu hỏi này quá mơ hồ. Tôi đang xóa mọi tham chiếu đến các DBMS khác và giới hạn các phản hồi đối với SqlServer. Nếu OP hoặc bất kỳ độc giả nào khác muốn thảo luận về giá trị của khái niệm cốt lõi này trên các nền tảng khác, thì chúng ta nên thảo luận một lần cho mỗi nền tảng. Thật bất lợi khi biến điều này thành một sự tham gia của cartesian, sẽ có quá nhiều chủ đề trò chuyện khác nhau trên một trang.
jcolebrand

Câu trả lời:


25

Câu trả lời này có thể hữu ích cho câu hỏi ban đầu nhưng chủ yếu là để giải quyết thông tin không chính xác trong các bài đăng khác. Nó cũng nêu bật một phần vô nghĩa trong BOL.

Và như đã nêu trong tài liệu INSERT , nó sẽ có được một khóa độc quyền trên bàn. Cách duy nhất CHỌN có thể được thực hiện đối với bảng là sử dụng NOLOCK hoặc đặt mức cô lập của giao dịch.

Phần được liên kết của các trạng thái BOL:

Một câu lệnh INSERT luôn có được một khóa (X) độc quyền trên bảng mà nó sửa đổi và giữ khóa đó cho đến khi giao dịch hoàn tất. Với khóa (X) độc quyền, không có giao dịch nào khác có thể sửa đổi dữ liệu; hoạt động đọc chỉ có thể diễn ra với việc sử dụng gợi ý NOLOCK hoặc đọc mức cô lập không được cam kết. Để biết thêm thông tin, hãy xem Khóa trong Cơ sở dữ liệu .

Lưu ý: Kể từ 2014-8-27 BOL đã được cập nhật để xóa các tuyên bố không chính xác được trích dẫn ở trên.

Rất may đây không phải là trường hợp. Nếu nó được chèn vào một bảng sẽ xảy ra nghiêm trọng và tất cả các độc giả sẽ bị chặn khỏi toàn bộ bảng cho đến khi giao dịch chèn hoàn tất. Điều đó sẽ làm cho SQL Server hiệu quả như một máy chủ cơ sở dữ liệu như NTFS. Không hẳn.

Tâm lý chung cho thấy điều đó không thể như vậy nhưng như Paul Randall chỉ ra rằng: " Hãy tự giúp mình, tin tưởng không ai ". Nếu bạn không thể tin tưởng bất cứ ai, kể cả BOL , tôi đoán chúng ta sẽ phải chứng minh điều đó.

Tạo một cơ sở dữ liệu và điền vào một bảng giả với một loạt các hàng, lưu ý rằng DatabaseId được trả về.

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

Thiết lập theo dõi hồ sơ sẽ theo dõi khóa: thu được và khóa: các sự kiện đã phát hành, lọc trên DatabaseId từ tập lệnh trước đó, đặt đường dẫn cho tệp và lưu ý TraceId được trả về.

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 TraceID=@TraceID
goto finish

error: 
select ErrorCode=@rc

finish: 
go

Chèn một hàng và dừng theo dõi:

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

Mở tệp theo dõi và bạn sẽ tìm thấy như sau:

Cửa sổ hồ sơ

Trình tự các khóa được thực hiện là:

  1. Khóa Intent-Exclusive trên MyTable
  2. Khóa Intent-Exclusive trên trang 1: 211
  3. RangeInsert-NullResource trên mục nhập chỉ mục được nhóm cho giá trị được chèn
  4. Khóa độc quyền trên chìa khóa

Các khóa sau đó được phát hành theo thứ tự ngược lại. Không có lúc nào có một khóa độc quyền được mua trên bàn.

Nhưng đây chỉ là một đợt chèn! Điều đó không giống như hai, ba hoặc hàng chục chạy song song.

Vâng, đúng vậy. SQL Server (và được cho là bất kỳ công cụ cơ sở dữ liệu quan hệ nào) không có tầm nhìn xa về những lô khác có thể đang chạy khi nó xử lý một câu lệnh và / hoặc lô, do đó trình tự thu thập khóa không thay đổi.

Điều gì về mức độ cô lập cao hơn, ví dụ như serializable?

Đối với ví dụ cụ thể này chính xác các khóa tương tự được thực hiện. Đừng tin tôi, hãy thử nó!


2
Rất nhiều thông tin. Làm tốt lắm @Mark!
jcolebrand

0

Tôi không làm nhiều công việc T-SQL nhưng từ việc đọc tài liệu ...

Đây là theo thiết kế, như đã nêu trong GIAO DỊCH BEGIN :

Tùy thuộc vào cài đặt mức cô lập giao dịch hiện tại, nhiều tài nguyên có được để hỗ trợ các câu lệnh Transact-SQL do kết nối phát hành bị khóa bởi giao dịch cho đến khi hoàn thành với câu lệnh GIAO DỊCH GIAO DỊCH hoặc ROLLBACK TRANSACTION.

Và như đã nêu trong tài liệu INSERT , nó sẽ có được một khóa độc quyền trên bàn. Cách duy nhất CHỌN có thể được thực hiện đối với bảng là sử dụng NOLOCKhoặc đặt mức cô lập của giao dịch.


4
Trước đây không nhận thấy rằng tuyên bố khá tệ trong BOL. Một khóa độc quyền trên một cái gì đó trong hệ thống phân cấp tài nguyên sẽ được yêu cầu nhưng chắc chắn không phải lúc nào cũng là bảng.
Mark Storey-Smith

6
-1 cho các tài liệu (không phải lỗi của bạn) - thật dễ dàng để chứng minh điều này không đúng trong cách ly ảnh chụp nhanh nên chăn "luôn luôn có khóa (X) độc quyền" là sai. Không chắc chắn về các mức cô lập khác.
Jack nói hãy thử topanswers.xyz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.