Khóa thang cuốn - Chuyện gì đang xảy ra ở đây?


138

Trong khi thay đổi bảng (xóa một cột) trong SQL Server 2008, tôi đã nhấp vào nút Tạo tập lệnh thay đổi và tôi nhận thấy rằng tập lệnh thay đổi mà nó tạo ra làm rơi cột, nói "đi" và sau đó chạy một câu lệnh ALTER TABLE bổ sung xuất hiện để đặt leo thang khóa cho bảng thành "BẢNG". Thí dụ:

ALTER TABLE dbo.Contract SET (LOCK_ESCALATION = TABLE)

Tôi cũng nên lưu ý rằng đây là điều cuối cùng mà kịch bản thay đổi đang thực hiện. Nó đang làm gì ở đây và tại sao nó lại đặt LOCK_ESCALATION thành BẢNG?

Câu trả lời:


165

" Khóa nâng cấp " là cách SQL xử lý khóa cho các bản cập nhật lớn. Khi SQL sẽ thay đổi nhiều hàng, công cụ cơ sở dữ liệu sẽ lấy ít khóa hơn, khóa lớn hơn (ví dụ như toàn bộ bảng) thay vì khóa nhiều thứ nhỏ hơn (ví dụ: khóa hàng).

Nhưng điều này có thể có vấn đề khi bạn có một bảng lớn, bởi vì việc khóa trên toàn bộ bảng có thể khóa các truy vấn khác trong một thời gian dài. Đó là sự đánh đổi: nhiều khóa có độ chi tiết nhỏ chậm hơn ít hơn (hoặc một) khóa hạt thô và việc có nhiều truy vấn khóa các phần khác nhau của bảng sẽ tạo ra khả năng bế tắc nếu một tiến trình đang chờ trên một tiến trình khác.

Có một tùy chọn cấp bảng LOCK_ESCALATION, mới trong SQL 2008, cho phép kiểm soát leo thang khóa. Mặc định, "BẢNG" cho phép các khóa tăng dần đến mức của bảng. DISABLE ngăn chặn sự leo thang khóa đến toàn bộ bảng trong hầu hết các trường hợp. AUTO cho phép khóa bảng trừ khi bảng được phân vùng, trong trường hợp đó khóa chỉ được tạo theo mức phân vùng. Xem bài đăng trên blog này để biết thêm.

Tôi nghi ngờ rằng IDE thêm cài đặt này khi tạo lại bảng vì TABLE là mặc định trong SQL 2008. Lưu ý rằng LOCK_ESCALATION không được hỗ trợ trong SQL 2005, vì vậy bạn sẽ cần phải loại bỏ nó nếu cố chạy tập lệnh trên Ví dụ năm 2005. Ngoài ra, vì TABLE là mặc định, bạn có thể xóa dòng đó một cách an toàn khi chạy lại tập lệnh của mình.

Cũng lưu ý rằng, trong SQL 2005 trước khi có cài đặt này, tất cả các khóa có thể leo thang đến mức bảng - nói cách khác, "TABLE" là cài đặt duy nhất trên SQL 2005.


1
Bài đăng trùng lặp trên Diễn đàn MSDN cũng vậy: social.msdn.microsoft.com/Forums/en-US/sqldatabaseengine/thread/ phỏng
Jonathan Kehayias

6
@dma_k - Tùy chọn này không liên quan CREATE TABLEvì bảng chưa tồn tại nên không có gì để khóa.
Justin Grant

1
Nhưng tại sao câu lệnh LOCK_ESCALATION sau câu lệnh ALTER TABLE ban đầu trong tập lệnh thay đổi khi thiết kế bảng trong SSMS? Chắc chắn đến lúc đó công việc đã được thực hiện rồi. Không nên trước khi thay đổi cấu trúc của bảng?
Kỹ sư đảo ngược

2
@DaveBoltman - SET là một phần của câu lệnh ALTER TABLE. Đó không phải là một tuyên bố riêng biệt. Xem docs.microsoft.com/en-us/sql/t-sql/statements/,
Justin Grant

2
JustinGrant, vẫn còn, câu hỏi từ @DaveBoltman đứng. Kịch bản mà SSMS tạo ra, giả sử, thêm một cột mới có hai ALTER TABLEcâu lệnh riêng biệt . Đầu tiên ALTER TABLE ADD column, sau đó GO, sau đó thứ hai ALTER TABLE SET LOCK_ESCALATION=TABLE, sau đó thứ hai GO. Vì vậy, LOCK_ESCALATIONđược đặt sau khi cột được thêm vào. Điểm thiết lập của nó sau khi thực tế là gì? Hai ALTER TABLEcâu lệnh này được gói trong một giao dịch, nhưng cột vẫn được thêm vào trước khi LOCK_ESCALATIONđược đặt. Tôi nghĩ rằng tôi sẽ đào sâu thêm một chút và viết một câu trả lời khác.
Vladimir Baranov

11

Bạn có thể kiểm tra xem bạn có cần đưa câu lệnh LOCK_ESCALATION vào tập lệnh của mình hay không bằng cách so sánh giá trị này trước và sau khi chạy phần chính của tập lệnh:

SELECT lock_escalation_desc FROM sys.tables WHERE name='yourtablename'

Trong trường hợp của tôi, việc thay đổi bảng để giảm hoặc thêm một ràng buộc dường như không làm thay đổi giá trị này.


11

Câu trả lời của Justin Grant giải thích những gì LOCK_ESCALATIONthiết lập nói chung, nhưng bỏ lỡ một chi tiết quan trọng và nó không giải thích tại sao SSMS tạo mã đặt nó. Đặc biệt, có vẻ rất lạ khi nó LOCK_ESCALATIONđược đặt làm tuyên bố cuối cùng trong kịch bản.

Tôi đã làm một vài bài kiểm tra và đây là sự hiểu biết của tôi về những gì đang xảy ra ở đây.

Phiên bản ngắn

Câu ALTER TABLElệnh thêm, giảm hoặc thay đổi một cột hoàn toàn lấy khóa sửa đổi lược đồ (SCH-M) trên bảng, điều này không liên quan gì đến việc LOCK_ESCALATIONthiết lập bảng. LOCK_ESCALATIONảnh hưởng đến hành vi khóa trong các báo cáo DML ( INSERT, UPDATE, DELETE, vv), không trong câu lệnh DDL ( ALTER). Khóa SCH-M luôn là khóa của toàn bộ đối tượng cơ sở dữ liệu, bảng trong ví dụ này.

Đây có thể là nơi sự nhầm lẫn đến từ.

SSMS thêm ALTER TABLE <TableName> SET (LOCK_ESCALATION = ...)câu lệnh vào tập lệnh của nó trong mọi trường hợp, ngay cả khi không cần thiết. Trong trường hợp khi cần câu lệnh này, nó được thêm vào để duy trì cài đặt hiện tại của bảng, không khóa bảng theo một cách cụ thể nào đó trong quá trình thay đổi lược đồ bảng xảy ra trong tập lệnh đó.

Nói cách khác, bảng được khóa bằng khóa SCH-M trên ALTER TABLE ALTER COLUMNcâu lệnh đầu tiên trong khi tất cả công việc thay đổi lược đồ bảng được thực hiện. ALTER TABLE SET LOCK_ESCALATIONTuyên bố cuối cùng không ảnh hưởng đến nó. Nó ảnh hưởng đến báo cáo DML chỉ trong tương lai ( INSERT, UPDATE,DELETE , vv) cho bảng đó.

Thoạt nhìn trông như thể SET LOCK_ESCALATION = TABLE có liên quan đến thực tế là chúng ta đang thay đổi toàn bộ bảng (chúng ta đang thay đổi lược đồ của nó ở đây), nhưng nó gây hiểu nhầm.

Phiên bản dài

Khi thay đổi bảng trong một số trường hợp, SSMS tạo ra một tập lệnh tạo lại toàn bộ bảng và trong một số trường hợp đơn giản hơn (như thêm hoặc thả một cột), tập lệnh không tạo lại bảng.

Hãy lấy bảng mẫu này làm ví dụ:

CREATE TABLE [dbo].[Test](
    [ID] [int] NOT NULL,
    [Col1] [nvarchar](50) NOT NULL,
    [Col2] [int] NOT NULL,
 CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

Mỗi bảng có một LOCK_ESCALATIONcài đặt, được đặt thành TABLEmặc định. Hãy thay đổi nó ở đây:

ALTER TABLE dbo.Test SET (LOCK_ESCALATION = DISABLE)

Bây giờ, nếu tôi cố gắng thay đổi Col1loại trong trình thiết kế bảng SSMS, SSMS sẽ tạo một tập lệnh tạo lại toàn bộ bảng:

BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_Test
    (
    ID int NOT NULL,
    Col1 nvarchar(10) NOT NULL,
    Col2 int NOT NULL
    )  ON [PRIMARY]
GO
ALTER TABLE dbo.Tmp_Test SET (LOCK_ESCALATION = DISABLE)
GO
IF EXISTS(SELECT * FROM dbo.Test)
     EXEC('INSERT INTO dbo.Tmp_Test (ID, Col1, Col2)
        SELECT ID, CONVERT(nvarchar(10), Col1), Col2 FROM dbo.Test WITH (HOLDLOCK TABLOCKX)')
GO
DROP TABLE dbo.Test
GO
EXECUTE sp_rename N'dbo.Tmp_Test', N'Test', 'OBJECT' 
GO
ALTER TABLE dbo.Test ADD CONSTRAINT
    PK_Test PRIMARY KEY CLUSTERED 
    (
    ID
    ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO
COMMIT

Bạn có thể thấy ở trên nó đặt LOCK_ESCALATIONcho bảng vừa tạo. SSMS thực hiện nó để duy trì cài đặt hiện tại của bảng. SSMS tạo dòng này, ngay cả khi giá trị hiện tại của cài đặt là TABLEgiá trị mặc định . Để đảm bảo an toàn và rõ ràng và ngăn ngừa các vấn đề có thể xảy ra trong tương lai nếu trong tương lai, sự thay đổi mặc định này, tôi đoán vậy. Điều này thật ý nghĩa.

Trong ví dụ này, thực sự cần thiết để tạo SET LOCK_ESCALATIONcâu lệnh, bởi vì bảng được tạo trước đó và cài đặt của nó phải được giữ nguyên.

Nếu tôi cố gắng thực hiện một thay đổi đơn giản cho bảng bằng trình thiết kế bảng SSMS, chẳng hạn như thêm một cột mới, thì SSMS sẽ tạo một tập lệnh không tạo lại bảng:

BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.Test ADD
    NewCol nchar(10) NULL
GO
ALTER TABLE dbo.Test SET (LOCK_ESCALATION = DISABLE)
GO
COMMIT

Như bạn có thể thấy, nó vẫn thêm ALTER TABLE SET LOCK_ESCALATIONtuyên bố, mặc dù trong trường hợp này nó không cần thiết chút nào. Cái đầu tiên ALTER TABLE ... ADDkhông thay đổi cài đặt hiện tại. Tôi đoán, các nhà phát triển SSMS đã quyết định rằng không đáng nỗ lực để cố gắng xác định trong trường hợp nào ALTER TABLE SET LOCK_ESCALATIONtuyên bố này là dư thừa và tạo ra nó luôn luôn, chỉ để an toàn. Không có hại trong việc thêm tuyên bố này mỗi lần.

Một lần nữa, LOCK_ESCALATIONcài đặt toàn bảng không liên quan trong khi lược đồ bảng thay đổi thông qua ALTER TABLEcâu lệnh. LOCK_ESCALATIONcài đặt chỉ ảnh hưởng đến hành vi khóa của các câu lệnh DML, như UPDATE.

Cuối cùng, một trích dẫn từ ALTER TABLE, nhấn mạnh của tôi:

Những thay đổi được chỉ định trong ALTER TABLE được triển khai ngay lập tức. Nếu các thay đổi yêu cầu sửa đổi các hàng trong bảng, ALTER TABLE cập nhật các hàng. ALTER TABLE có được khóa sửa đổi lược đồ (SCH-M) trên bảng để đảm bảo rằng không có kết nối nào khác tham chiếu ngay cả siêu dữ liệu cho bảng trong khi thay đổi, ngoại trừ các hoạt động chỉ mục trực tuyến yêu cầu khóa SCH-M rất ngắn ở cuối. Trong thao tác ALTER TABLE vĩ đại SWITCH, khóa được lấy trên cả bảng nguồn và bảng đích. Các sửa đổi được thực hiện cho bảng được ghi lại và có thể phục hồi hoàn toàn. Các thay đổi ảnh hưởng đến tất cả các hàng trong các bảng rất lớn, chẳng hạn như bỏ một cột hoặc, trên một số phiên bản của SQL Server, việc thêm cột KHÔNG NULL với giá trị mặc định, có thể mất nhiều thời gian để hoàn thành và tạo nhiều bản ghi nhật ký. Các câu lệnh ALTER TABLE này phải được thực thi với cùng một cách chăm sóc như bất kỳ câu lệnh INSERT, UPDATE hoặc DELETE nào ảnh hưởng đến nhiều hàng.

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.