Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Защо тази заявка е бавна първия път след като стартирам услугата?

Аз мога също може да възпроизведе това 100% от времето на моята машина. (вижте бележката в края)

Същността на проблема е, че изваждате S заключва редовете на системната таблица в tempdb които могат да са в конфликт с ключалките, необходими за вътрешна tempdb транзакции за почистване.

Когато тази работа по почистване е разпределена към същата сесия, която притежава S заключване може да възникне неопределено увисване.

За да избегнете със сигурност този проблем, трябва да спрете да препращате към system обекти в tempdb .

Възможно е да се създаде таблица с числа, без изобщо да се препраща към външни таблици. Следното не трябва да чете редове на базовата таблица и по този начин също не приема заключвания.

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 

Стъпки за възпроизвеждане

Първо създайте процедура

CREATE PROC P
AS
    SET NOCOUNT ON;

    DECLARE @T TABLE (X INT)
GO

След това рестартирайте SQL услугата и в една връзка изпълнете

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 

След това стартирайте в друга връзка

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

Заявката, попълваща таблицата Numbers, изглежда успява да влезе в ситуация на заключване на живо с вътрешните системни транзакции, които почистват временни обекти, като например променливи на таблица.

Успях да блокирам сесия ID 53 по този начин. Той е блокиран за неопределено време. Резултатът от sp_WhoIsActive показва, че този паяк прекарва почти цялото си време в спряно състояние. При последователни изпълнения числата в reads колона се увеличава, но стойностите в другите колони остават до голяма степен същите.

Продължителността на изчакване не показва нарастващ модел, но показва, че трябва да се деблокира периодично, преди да бъде блокиран отново.

SELECT *
FROM   sys.dm_os_waiting_tasks
WHERE  session_id = blocking_session_id

Връщане

+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
| 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 |
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+

Използване на идентификатора в описанието на ресурса

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 

Връщане

+------------+
|    name    |
+------------+
| sysschobjs |
+------------+

Работи

SELECT resource_description,request_status
FROM   sys.dm_tran_locks 
WHERE request_session_id = 53 AND request_status <> 'GRANT'

Връщане

+----------------------+----------------+
| resource_description | request_status |
+----------------------+----------------+
| (246708db8c1f)       | CONVERT        |
+----------------------+----------------+

Свързва се чрез DAC и работи

SELECT id,name
FROM   tempdb.sys.sysschobjs WITH (NOLOCK)
WHERE %%LOCKRES%% = '(246708db8c1f)' 

Връщане

+-------------+-----------+
|     id      |   name    |
+-------------+-----------+
| -1578606288 | #A1E86130 |
+-------------+-----------+

Любопитно какво е това

SELECT name,user_type_id
FROM tempdb.sys.columns
WHERE object_id = -1578606288 

Връщане

+------+--------------+
| name | user_type_id |
+------+--------------+
| X    |           56 |
+------+--------------+

Това е името на колоната в променливата на таблицата, използвана от съхранената процедура.

Работи

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)' 

Връщане

+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
| 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 |
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+

Така че SELECT INTO транзакцията съдържа S заключване на реда в tempdb.sys.sysschobjs отнасящи се до променливата на таблицата #A1E86130 . droptemp транзакцията не може да получи X заключване на този ред поради това противоречиво S заключване.

Изпълнението на тази заявка многократно разкрива, че transaction_id за droptemp транзакцията многократно се променя.

Предполагам, че SQL Server трябва да разпредели тези вътрешни транзакции на потребителски spids и да ги приоритизира, преди да извърши работата на потребителя. Така че идентификаторът на сесията 53 е блокиран в постоянен цикъл, където стартира droptemp транзакция, се блокира от потребителската транзакция, изпълнявана на същия spid. Връща назад вътрешната транзакция, след което повтаря процеса за неопределено време.

Това се потвърждава от проследяване на различните заключващи и транзакционни събития в SQL Server Profiler, след като spid се окачва.

Също така проследих заключващите събития преди това.

Блокиране на заключващи събития

Повечето от ключалките със споделен ключ са извадени от SELECT INTO транзакция на ключове в sysschobjs освободете незабавно. Изключение е първото заключване на (246708db8c1f) .

Това има известен смисъл, тъй като планът показва сканиране на вложени цикли на [sys].[sysschobjs].[clst] [o] и тъй като на временните обекти се дават отрицателни objectid, те ще бъдат първите срещани редове в реда на сканиране.

Също така се сблъсках със ситуацията, описана в OP, където стартирането на трипосочно кръстосано съединение първо изглежда позволява на четирипосочното едно да успее.

Първите няколко събития в трасирането за SELECT INTO транзакция има съвсем различен модел.

Това беше след рестартиране на услугата, така че стойностите на ресурса за заключване в колоната с текстови данни не са директно сравними.

Вместо да се запази ключалката на първия ключ и след това модел на придобиване и освобождаване на следващите ключове, изглежда се придобиват много повече брави, без да се освобождават първоначално.

Предполагам, че трябва да има някаква разлика в стратегията за изпълнение, която избягва проблема.

Актуализиране

Елементът Connect, който повдигнах за това не е маркиран като фиксиран, но сега съм на SQL Server 2012 SP2 и сега мога да възпроизвеждам само временно самоблокиране, а не постоянно. Все още получавам самоблокиране, но след известен брой неуспешни опити да изпълня droptemp транзакцията е успешна, изглежда, че се връща към обработката на потребителската транзакция. След това се ангажира системната транзакция, след което се изпълнява успешно. Все още на същия spid. (осем опита в едно примерно изпълнение. Не съм сигурен дали това ще се повтаря постоянно)



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Отстраняване на проблеми с репликацията на транзакции на SQL Server

  2. Разгръщане на LocalDB на клиентски компютър

  3. JPA GenerationType.AUTO не отчита колона с автоматично нарастване

  4. Обработвайте множество db актуализации от c# в SQL Server 2008

  5. Как да конвертирате редове в колони в SQL Server?