Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Tại sao truy vấn này chậm lần đầu tiên sau khi tôi bắt đầu dịch vụ?

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)



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Siêu liên kết một giá trị trong kết quả truy vấn SQL Server trong SSMS

  2. thêm + - 1 năm trong SQL Server

  3. Cột được tính toán trong SQL Server là gì?

  4. Thay thế toàn bộ từ bằng cách sử dụng thay thế máy chủ ms sql

  5. Nơi sử dụng Áp dụng bên ngoài