Một trong những trường hợp bế tắc ít phổ biến hơn là trường hợp có một người dùng duy nhất và họ tự bế tắc trên một số tài nguyên hệ thống. Một điều gần đây tôi đã gặp là tạo một kiểu bí danh, sau đó khai báo một biến thuộc kiểu đó, bên trong cùng một giao dịch. Hãy tưởng tượng bạn đang cố gắng chạy thử nghiệm đơn vị hoặc thử nghiệm trước khi triển khai, kiểm tra lỗi và khôi phục trong mọi trường hợp để bạn không để lại bất kỳ dấu vết nào về những gì bạn đã làm. Mẫu có thể trông như thế này:
BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO DECLARE @x TABLE (e EmailAddress); GO ROLLBACK TRANSACTION;
Hoặc, nhiều khả năng, phức tạp hơn một chút:
BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; DECLARE @x TABLE (e EmailAddress); INSERT @x SELECT @param; END GO DECLARE @x EmailAddress; SET @x = N'whatever'; EXEC dbo.foo @param = N'whatever'; GO ROLLBACK TRANSACTION;
Nơi đầu tiên tôi thử mã này là SQL Server 2012 và cả hai ví dụ đều không thành công với lỗi sau:
Msg 1205, Mức 13, Trạng thái 55, Dòng 14Giao dịch (ID quy trình 57) đã bị khóa trên tài nguyên khóa với một quy trình khác và đã được chọn làm nạn nhân của bế tắc. Chạy lại giao dịch.
Và không có nhiều thứ để học từ biểu đồ bế tắc:
Quay trở lại vài năm, tôi nhớ lại khi lần đầu tiên tôi học về các kiểu bí danh, trong SQL Server 2000 (khi chúng được gọi là Kiểu dữ liệu do người dùng xác định). Vào thời điểm đó, bế tắc này mà tôi gặp gần đây sẽ không xảy ra (nhưng điều này ít nhất một phần là do bạn không thể khai báo một biến bảng với kiểu bí danh - xem tại đây và tại đây). Tôi đã chạy mã sau trên SQL Server 2000 RTM (8.0.194) và SQL Server 2000 SP4 (8.0.2039) và nó chạy tốt:
BEGIN TRANSACTION; GO EXEC sp_addtype @typename = N'EmailAddress', @phystype = N'VARCHAR(320)'; GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; SELECT @param; END GO EXEC dbo.foo @param = N'whatever'; GO DECLARE @x EmailAddress; SET @x = N'whatever'; EXEC dbo.foo @param = @x; GO ROLLBACK TRANSACTION;
Tất nhiên, kịch bản này không phổ biến vào thời điểm đó bởi vì suy cho cùng, không có nhiều người sử dụng các loại bí danh ngay từ đầu. Mặc dù chúng có thể làm cho siêu dữ liệu của bạn giống như tự ghi lại dữ liệu hơn và giống như định nghĩa dữ liệu, nhưng chúng là một nỗi lo lắng nếu bạn muốn thay đổi chúng, đây có thể là một chủ đề cho một bài đăng khác.
SQL Server 2005 xuất hiện và giới thiệu cú pháp DDL mới để tạo các kiểu bí danh:CREATE TYPE
. Điều này không thực sự giải quyết được vấn đề với việc thay đổi kiểu, nó chỉ làm cho cú pháp gọn gàng hơn một chút. Trong RTM, tất cả các mẫu mã trên đều hoạt động tốt mà không gặp phải bế tắc. Trong SP4, tuy nhiên, tất cả chúng sẽ bế tắc. Do đó, ở đâu đó giữa RTM và SP4, họ đã thay đổi cách xử lý nội bộ đối với các giao dịch liên quan đến các biến bảng bằng cách sử dụng các loại bí danh.
Tua nhanh vài năm tới SQL Server 2008, nơi các tham số có giá trị bảng đã được thêm vào (xem trường hợp sử dụng tốt ở đây). Điều này làm cho việc sử dụng các loại này trở nên phổ biến hơn nhiều và đưa ra một trường hợp khác trong đó một giao dịch cố gắng tạo và sử dụng một loại như vậy sẽ bị bế tắc:
BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO DECLARE @r dbo.Items; GO ROLLBACK TRANSACTION;
Tôi đã kiểm tra Kết nối và tìm thấy một số mục liên quan, một trong số đó cho rằng sự cố này đã được khắc phục trong SQL Server 2008 SP2 và 2008 R2 SP1:
Connect # 365876:Chốt lại xảy ra khi tạo kiểu dữ liệu do người dùng xác định và các đối tượng sử dụng nó
Điều này thực sự đề cập đến tình huống sau, trong đó chỉ cần tạo một thủ tục được lưu trữ tham chiếu đến kiểu trong một biến bảng sẽ bị bế tắc trong SQL Server 2008 RTM (10.0.1600) và SQL Server 2008 R2 RTM (10.50.1600):
BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; DECLARE @x TABLE (e EmailAddress); INSERT @x SELECT @param; END GO ROLLBACK TRANSACTION;
Tuy nhiên, điều này không bế tắc trong SQL Server 2008 SP3 (10.0.5846) hoặc 2008 R2 SP2 (10.50.4295). Vì vậy, tôi có xu hướng tin vào các nhận xét trên mục Kết nối - rằng phần lỗi này đã được sửa trong 2008 SP2 và 2008 R2 SP1 và chưa bao giờ là vấn đề trong các phiên bản hiện đại hơn.
Nhưng điều này vẫn làm mất khả năng thực sự đặt loại bí danh thông qua bất kỳ loại thử nghiệm thực sự nào. Vì vậy, các bài kiểm tra đơn vị của tôi sẽ thành công miễn là tất cả những gì tôi muốn làm là kiểm tra xem tôi có thể tạo thủ tục hay không - hãy quên việc khai báo kiểu dưới dạng biến cục bộ hoặc dưới dạng cột trong biến bảng cục bộ.
Cách duy nhất để giải quyết vấn đề này là tạo loại bảng trước khi bắt đầu giao dịch và loại bỏ nó một cách rõ ràng sau đó (hoặc nếu không thì chia nó thành nhiều giao dịch). Điều này có thể cực kỳ cồng kềnh hoặc thậm chí là không thể thực hiện được, các khung và khai thác kiểm tra tự động thường thay đổi hoàn toàn cách chúng hoạt động để giải thích cho hạn chế này.
Vì vậy, tôi quyết định thực hiện một số thử nghiệm trong các bản dựng ban đầu và gần đây nhất của tất cả các phiên bản chính:SQL Server 2005 RTM, 2005 SP4, 2008 RTM, 2008 SP3, 2008 R2 RTM, 2008 R2 SP2, 2012 RTM, 2012 SP1, và 2014 CTP2 (và vâng, tôi đã cài đặt tất cả chúng). Tôi đã xem xét một số mục Connect và nhiều nhận xét khác nhau khiến tôi băn khoăn không biết trường hợp sử dụng nào được hỗ trợ và ở đâu, và tôi có một sự bắt buộc kỳ lạ để tìm hiểu xem khía cạnh nào của vấn đề này đã thực sự được khắc phục. Tôi đã thử nghiệm các tình huống bế tắc tiềm năng khác nhau liên quan đến các loại bí danh, biến bảng và các tham số có giá trị bảng dựa trên tất cả các bản dựng này; mã như sau:
/* alias type - declare in local table variable always deadlocks on 2005 SP4 -> 2014, except in 2005 RTM */ BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320) GO DECLARE @r TABLE(e EmailAddress); GO ROLLBACK TRANSACTION; /* alias type - create procedure with param & table var sometimes deadlocks - 2005 SP4, 2008 RTM & SP1, 2008 R2 RTM */ BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; DECLARE @x TABLE (e EmailAddress); INSERT @x SELECT @param; END GO ROLLBACK TRANSACTION; /* alias type - create procedure, declare & exec always deadlocks on 2005 SP4 -> 2014, except on 2005 RTM */ BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; DECLARE @x TABLE (e EmailAddress); INSERT @x SELECT @param; END GO DECLARE @x EmailAddress; SET @x = N'whatever'; EXEC dbo.foo @param = N'whatever'; GO ROLLBACK TRANSACTION; /* obviously did not run these on SQL Server 2005 builds */ /* table type - create & declare local variable always deadlocks on 2008 -> 2014 */ BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO DECLARE @r dbo.Items; GO ROLLBACK TRANSACTION; /* table type - create procedure with param and SELECT never deadlocks on 2008 -> 2014 */ BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO CREATE PROCEDURE dbo.foo @param dbo.Items READONLY AS BEGIN SET NOCOUNT ON; SELECT Item FROM @param; END GO ROLLBACK TRANSACTION; /* table type - create procedure, declare & exec always deadlocks on 2008 -> 2014 */ BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO CREATE PROCEDURE dbo.foo @param dbo.Items READONLY AS BEGIN SET NOCOUNT ON; SELECT Item FROM @param; END GO DECLARE @x dbo.Items; EXEC dbo.foo @param = @x; GO ROLLBACK TRANSACTION;
Và kết quả phản ánh câu chuyện của tôi ở trên:SQL Server 2005 RTM không bế tắc trong bất kỳ tình huống nào, nhưng vào thời điểm SP4 quay vòng, tất cả chúng đều bị bế tắc. Điều này đã được sửa chữa cho kịch bản "tạo một loại và tạo một thủ tục", nhưng không có kịch bản nào khác, trong 2008 SP2 và 2008 R2 SP1. Đây là bảng hiển thị tất cả các kết quả:
Phiên bản SQL Server / Bản dựng # | ||||||||||
SQL 2005 | SQL 2008 | SQL 2008 R2 | SQL 2012 | SQL 2014 | ||||||
RTM 9.0.1399 | SP4 9.0.5324 | RTM 10.0.1600 | SP3 10.0.5846 | RTM 10.50.1600 | SP2 10.50.4295 | RTM 11.0.2100 | SP1 11.0.3381 | CTP2 12.0.1524 | ||
| khai báo trong bảng var | |||||||||
tạo thủ tục | ||||||||||
tạo &thực thi chương trình | ||||||||||
| khai báo var địa phương | Không áp dụng | ||||||||
tạo thủ tục | ||||||||||
tạo &thực thi chương trình |
Kết luận
Vì vậy, đạo đức của câu chuyện là, vẫn không có cách khắc phục nào cho trường hợp sử dụng được mô tả ở trên, nơi bạn muốn tạo một loại bảng, tạo một thủ tục hoặc hàm sử dụng loại, khai báo một loại, kiểm tra mô-đun và cuộn mọi thứ trở lại. Trong mọi trường hợp, đây là các mục Kết nối khác để bạn xem; hy vọng bạn có thể bỏ phiếu cho họ và để lại nhận xét mô tả kịch bản bế tắc này ảnh hưởng trực tiếp đến doanh nghiệp của bạn như thế nào:
Tôi hoàn toàn mong đợi một số thông tin làm rõ sẽ được thêm vào các mục Connect này trong tương lai gần, mặc dù tôi không biết chính xác khi nào chúng sẽ được hoàn thiện.