Tôi có thể cũng có thể tái tạo 100% thời gian này trên máy của tôi. (xem ghi chú ở cuối)
Ý chính của vấn đề là bạn đang lấy ra S
khóa các hàng trong bảng hệ thống trong tempdb
có thể xung đột với các khóa cần thiết cho tempdb
nội bộ giao dịch dọn dẹp.
Khi công việc dọn dẹp này được phân bổ cho cùng một phiên sở hữu S
khóa, treo vô thời hạn có thể xảy ra.
Để tránh sự cố này, bạn cần dừng tham chiếu hệ thống system
các đối tượng bên trong tempdb
.
Có thể tạo một bảng số mà không cần tham chiếu đến bất kỳ bảng bên ngoài nào. Phần sau không cần đọc các hàng trong bảng cơ sở và do đó cũng không cần khóa.
WITH Ten(N) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
SELECT TOP 1000000 IDENTITY(INT, 1, 1) Number
INTO Numbers
FROM Ten T10,
Ten T100,
Ten T1000,
Ten T10000,
Ten T100000,
Ten T1000000
Các bước để tạo lại
Đầu tiên, hãy tạo một thủ tục
CREATE PROC P
AS
SET NOCOUNT ON;
DECLARE @T TABLE (X INT)
GO
Sau đó, khởi động lại Dịch vụ SQL và thực thi trong một kết nối
WHILE NOT EXISTS(SELECT *
FROM sys.dm_os_waiting_tasks
WHERE session_id = blocking_session_id)
BEGIN
/*This will cause the problematic droptemp transactions*/
EXEC sp_recompile 'P'
EXEC P
END;
SELECT *
FROM sys.dm_os_waiting_tasks
WHERE session_id = blocking_session_id
Sau đó, trong một kết nối khác chạy
USE tempdb;
SELECT TOP 1000000 IDENTITY(INT, 1, 1) Number
INTO #T
FROM sys.objects s1
CROSS JOIN sys.objects s2
CROSS JOIN sys.objects s3
CROSS JOIN sys.objects s4;
DROP TABLE #T
Truy vấn điền bảng Numbers dường như quản lý để rơi vào tình trạng khóa trực tiếp với các giao dịch hệ thống nội bộ làm sạch các đối tượng tạm thời như biến bảng.
Tôi đã quản lý để bị chặn phiên id 53 theo cách này. Nó bị chặn vô thời hạn. Đầu ra của sp_WhoIsActive
cho thấy rằng spid này hầu như dành toàn bộ thời gian bị treo. Chạy liên tiếp các số trong reads
cột tăng nhưng giá trị trong các cột khác hầu như không thay đổi.
Thời gian chờ không có biểu hiện ngày càng tăng mặc dù cho thấy rằng nó phải được bỏ chặn định kỳ trước khi bị chặn lại.
SELECT *
FROM sys.dm_os_waiting_tasks
WHERE session_id = blocking_session_id
Trả lại
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
| waiting_task_address | session_id | exec_context_id | wait_duration_ms | wait_type | resource_address | blocking_task_address | blocking_session_id | blocking_exec_context_id | resource_description |
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
| 0x00000002F2C170C8 | 53 | 0 | 86 | LCK_M_X | 0x00000002F9B13040 | 0x00000002F2C170C8 | 53 | NULL | keylock hobtid=281474978938880 dbid=2 id=lock2f9ac8880 mode=U associatedObjectId=281474978938880 |
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
Sử dụng id trong mô tả tài nguyên
SELECT o.name
FROM sys.allocation_units au WITH (NOLOCK)
INNER JOIN sys.partitions p WITH (NOLOCK)
ON au.container_id = p.partition_id
INNER JOIN sys.all_objects o WITH (NOLOCK)
ON o.object_id = p.object_id
WHERE allocation_unit_id = 281474978938880
Trả lại
+------------+
| name |
+------------+
| sysschobjs |
+------------+
Đang chạy
SELECT resource_description,request_status
FROM sys.dm_tran_locks
WHERE request_session_id = 53 AND request_status <> 'GRANT'
Trả lại
+----------------------+----------------+
| resource_description | request_status |
+----------------------+----------------+
| (246708db8c1f) | CONVERT |
+----------------------+----------------+
Kết nối qua DAC và chạy
SELECT id,name
FROM tempdb.sys.sysschobjs WITH (NOLOCK)
WHERE %%LOCKRES%% = '(246708db8c1f)'
Trả lại
+-------------+-----------+
| id | name |
+-------------+-----------+
| -1578606288 | #A1E86130 |
+-------------+-----------+
Tò mò về điều đó là gì
SELECT name,user_type_id
FROM tempdb.sys.columns
WHERE object_id = -1578606288
Trả lại
+------+--------------+
| name | user_type_id |
+------+--------------+
| X | 56 |
+------+--------------+
Đây là tên cột trong biến bảng được sử dụng bởi proc được lưu trữ.
Đang chạy
SELECT request_mode,
request_status,
request_session_id,
request_owner_id,
lock_owner_address,
t.transaction_id,
t.name,
t.transaction_begin_time
FROM sys.dm_tran_locks l
JOIN sys.dm_tran_active_transactions t
ON l.request_owner_id = t.transaction_id
WHERE resource_description = '(246708db8c1f)'
Trả lại
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
| request_mode | request_status | request_session_id | request_owner_id | lock_owner_address | transaction_id | name | transaction_begin_time |
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
| U | GRANT | 53 | 227647 | 0x00000002F1EF6800 | 227647 | droptemp | 2013-11-24 18:36:28.267 |
| S | GRANT | 53 | 191790 | 0x00000002F9B16380 | 191790 | SELECT INTO | 2013-11-24 18:21:30.083 |
| X | CONVERT | 53 | 227647 | 0x00000002F9B12FC0 | 227647 | droptemp | 2013-11-24 18:36:28.267 |
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
Vì vậy, SELECT INTO
giao dịch đang giữ một S
khóa hàng trong tempdb.sys.sysschobjs
liên quan đến biến bảng #A1E86130
. droptemp
giao dịch không thể nhận được X
khóa hàng này vì S
xung đột này khóa.
Chạy truy vấn này nhiều lần cho thấy rằng transaction_id
cho droptemp
giao dịch liên tục thay đổi.
Tôi suy đoán rằng SQL Server phải phân bổ các giao dịch nội bộ này trên các spid của người dùng và ưu tiên chúng trước khi thực hiện công việc của người dùng. Vì vậy, id phiên 53 bị kẹt trong một chu kỳ không đổi nơi nó khởi động droptemp
giao dịch, bị chặn bởi giao dịch của người dùng chạy trên cùng một spid. Cuộn lại giao dịch nội bộ sau đó lặp lại quy trình vô thời hạn.
Điều này xảy ra bằng cách theo dõi các sự kiện khóa và giao dịch khác nhau trong SQL Server Profiler sau khi spid bị treo.
Tôi cũng đã theo dõi các sự kiện khóa trước đó.
Khóa Chặn sự kiện
Hầu hết các khóa chìa khóa chia sẻ được thực hiện bởi SELECT INTO
giao dịch trên các khóa trong sysschobjs
được phát hành ngay lập tức. Ngoại lệ là khóa đầu tiên trên (246708db8c1f)
.
Điều này có ý nghĩa vì kế hoạch hiển thị các lần quét các vòng lồng nhau của [sys].[sysschobjs].[clst] [o]
và bởi vì các đối tượng tạm thời được cung cấp các đối tượng phủ định, chúng sẽ là các hàng đầu tiên gặp phải trong thứ tự quét.
Tôi cũng gặp phải tình huống được mô tả trong OP khi chạy phép nối chéo ba đường trước dường như cho phép bốn cách một thành công.
Một số sự kiện đầu tiên trong dấu vết cho SELECT INTO
giao dịch có một mô hình hoàn toàn khác.
Đây là sau khi khởi động lại dịch vụ nên các giá trị tài nguyên khóa trong cột dữ liệu văn bản không thể so sánh trực tiếp.
Thay vì giữ lại ổ khóa trên chìa khóa đầu tiên và sau đó là mô hình mua và phát hành các chìa khóa tiếp theo, nó dường như nhận được nhiều ổ khóa hơn mà không cần giải phóng chúng ban đầu.
Tôi cho rằng phải có một số khác biệt trong chiến lược thực thi để tránh vấn đề này.
Cập nhật
Mục kết nối tôi đã nêu ra về điều này
vẫn chưa được đánh dấu là đã sửa nhưng tôi hiện đang sử dụng SQL Server 2012 SP2 và hiện chỉ có thể tái tạo tự chặn tạm thời thay vì vĩnh viễn. Tôi vẫn nhận được tự chặn nhưng sau một số lần thử không thực hiện được droptemp
giao dịch thành công, nó dường như quay trở lại để xử lý giao dịch của người dùng. Sau đó cam kết giao dịch hệ thống sau đó được thực hiện thành công. Vẫn trên cùng một spid. (tám lần thử trong một lần chạy ví dụ. Tôi không chắc liệu điều này có lặp lại liên tục hay không)