Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Dlaczego to zapytanie jest powolne za pierwszym razem po uruchomieniu usługi?

mogę może również odtworzyć to w 100% na moim komputerze. (patrz uwaga na końcu)

Istotą problemu jest to, że wyjmujesz S blokuje wiersze tabeli systemowej w tempdb które mogą kolidować z blokadami potrzebnymi do wewnętrznego tempdb transakcje czyszczenia.

Gdy ta praca porządkowa jest przydzielona do tej samej sesji, która jest właścicielem S może nastąpić zawieszenie się na czas nieokreślony.

Aby na pewno uniknąć tego problemu, musisz przestać odwoływać się do system obiekty wewnątrz tempdb .

Możliwe jest utworzenie tabeli liczb bez odwoływania się w ogóle do tabel zewnętrznych. Następujące elementy nie muszą czytać wierszy tabeli podstawowej, a zatem również nie wymagają blokad.

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 

Kroki do odtworzenia

Najpierw utwórz procedurę

CREATE PROC P
AS
    SET NOCOUNT ON;

    DECLARE @T TABLE (X INT)
GO

Następnie uruchom ponownie usługę SQL i w jednym połączeniu wykonaj

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 

Następnie uruchom inne połączenie

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

Zapytanie wypełniające tabelę Numbers wydaje się być w stanie wejść w sytuację blokady na żywo z wewnętrznymi transakcjami systemowymi, które czyszczą obiekty tymczasowe, takie jak zmienne tabeli.

Udało mi się w ten sposób zablokować identyfikator sesji 53. Jest zablokowany na czas nieokreślony. Dane wyjściowe sp_WhoIsActive pokazuje, że ten pająk prawie cały czas spędza w zawieszeniu. W kolejnych przebiegach liczby w reads kolumna wzrasta, ale wartości w pozostałych kolumnach pozostają w dużej mierze takie same.

Czas oczekiwania nie wykazuje rosnącego wzorca, ale wskazuje, że musi być okresowo odblokowywany, zanim zostanie ponownie zablokowany.

SELECT *
FROM   sys.dm_os_waiting_tasks
WHERE  session_id = blocking_session_id

Zwroty

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

Korzystanie z identyfikatora w opisie zasobu

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 

Zwroty

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

Bieganie

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

Zwroty

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

Podłączanie przez DAC i uruchamianie

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

Zwroty

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

Ciekawe, co to jest

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

Zwroty

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

To jest nazwa kolumny w zmiennej tabeli używanej przez przechowywaną procedurę.

Bieganie

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

Zwroty

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

Więc SELECT INTO transakcja zawiera S zablokuj wiersz w tempdb.sys.sysschobjs odnoszące się do zmiennej tabeli #A1E86130 . droptemp transakcja nie może uzyskać X zablokować ten wiersz z powodu konfliktu S zablokuj.

Wielokrotne uruchamianie tego zapytania pokazuje, że transaction_id dla droptemp transakcja wielokrotnie się zmienia.

Spekuluję, że SQL Server musi alokować te wewnętrzne transakcje do spidów użytkowników i nadawać im priorytety przed wykonaniem pracy z użytkownikiem. Tak więc identyfikator sesji 53 utknął w stałym cyklu, w którym uruchamia droptemp transakcja, jest blokowana przez transakcję użytkownika działającą na tym samym spid. Wycofuje wewnętrzną transakcję, a następnie powtarza proces w nieskończoność.

Potwierdza to śledzenie różnych zdarzeń blokowania i transakcji w SQL Server Profiler po zawieszeniu się spid.

Śledziłem również wcześniejsze zdarzenia blokowania.

Zablokuj blokowanie zdarzeń

Większość wspólnych blokad kluczy usuwanych przez SELECT INTO transakcja na kluczach w sysschobjs zostać natychmiast zwolniony. Wyjątkiem jest pierwsza blokada na (246708db8c1f) .

Ma to sens, ponieważ plan pokazuje skany zagnieżdżonych pętli [sys].[sysschobjs].[clst] [o] a ponieważ obiekty tymczasowe otrzymują ujemne identyfikatory obiektów, będą pierwszymi wierszami napotkanymi w kolejności skanowania.

Zetknąłem się również z sytuacją opisaną w PO, gdzie uruchomienie trójstronnego łączenia krzyżowego jako pierwsze wydaje się pozwolić na odniesienie sukcesu czterokierunkowego.

Kilka pierwszych zdarzeń w śladzie dla SELECT INTO transakcja ma zupełnie inny wzór.

Było to po ponownym uruchomieniu usługi, więc wartości zasobów blokady w kolumnie danych tekstowych nie są bezpośrednio porównywalne.

Zamiast zachowywać blokadę na pierwszym kluczu, a następnie wzorzec zdobywania i zwalniania kolejnych kluczy, wydaje się, że uzyskuje znacznie więcej zamków bez ich początkowego zwalniania.

Zakładam, że musi istnieć jakaś rozbieżność w strategii wykonania, która pozwoli uniknąć problemu.

Aktualizacja

Zgłoszony przeze mnie element Connect o tym nie został oznaczony jako naprawiony, ale obecnie korzystam z dodatku SP2 dla programu SQL Server 2012 i mogę teraz odtwarzać tylko tymczasowe samoblokowanie, a nie stałe. Nadal otrzymuję samoblokowanie, ale po pewnej liczbie nieudanych prób wykonania droptemp transakcja pomyślnie wydaje się, że wraca do przetwarzania transakcji użytkownika. Po tym zatwierdza transakcja systemowa, a następnie jest wykonywana pomyślnie. Wciąż na tym samym spidzie. (osiem prób w jednym przykładowym przebiegu. Nie jestem pewien, czy będzie to powtarzane konsekwentnie)



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Rozwiązywanie problemów z replikacją transakcyjną programu SQL Server

  2. Wydajność zewnętrznej aplikacji z funkcją

  3. SQL 2008 R2 — wyzwalacze czasowe?

  4. 5 sposobów na uzyskanie skróconej nazwy miesiąca z daty w SQL Server

  5. Dodaj przesunięcie strefy czasowej do wartości datetime2 w programie SQL Server (T-SQL)