sql >> Databáze >  >> RDS >> Sqlserver

Proč je tento dotaz pomalý poprvé po spuštění služby?

můžu mohl také reprodukovat 100% času na mém počítači. (viz poznámka na konci)

Podstatou problému je, že vyjímáte S uzamkne řádky systémové tabulky v tempdb které mohou být v konfliktu se zámky potřebnými pro interní tempdb transakce čištění.

Když je toto čištění přiděleno stejné relaci, která vlastní S lock může dojít k neurčitému zablokování.

Chcete-li se tomuto problému s jistotou vyhnout, musíte přestat odkazovat na system objekty uvnitř tempdb .

Je možné vytvořit číselnou tabulku bez odkazování na externí tabulky. Následující text nepotřebuje číst žádné řádky základní tabulky, a proto také nepoužívá žádné zámky.

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 

Kroky pro reprodukci

Nejprve vytvořte proceduru

CREATE PROC P
AS
    SET NOCOUNT ON;

    DECLARE @T TABLE (X INT)
GO

Poté restartujte službu SQL a v jednom připojení proveďte

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 

Pak v dalším připojení spustit

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

Zdá se, že dotazu vyplňujícímu tabulku Numbers se podařilo dostat do situace živého uzamčení s interními systémovými transakcemi, které čistí dočasné objekty, jako jsou proměnné tabulky.

Tímto způsobem se mi podařilo zablokovat ID relace 53. Je blokován na dobu neurčitou. Výstup sp_WhoIsActive ukazuje, že tento spid tráví téměř všechen čas pozastaven. V po sobě jdoucích běhech čísla v reads sloupec se zvyšuje, ale hodnoty v ostatních sloupcích zůstávají z velké části stejné.

Doba čekání nevykazuje rostoucí vzor, ​​i když naznačuje, že musí být pravidelně odblokována, než bude znovu zablokována.

SELECT *
FROM   sys.dm_os_waiting_tasks
WHERE  session_id = blocking_session_id

Vrátí

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

Pomocí id v popisu zdroje

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 

Vrátí

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

Běh

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

Vrátí

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

Připojení přes DAC a spuštění

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

Vrátí

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

Zajímalo by mě, co to je

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

Vrátí

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

Toto je název sloupce v proměnné tabulky používané uloženým procesem.

Běh

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

Vrátí

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

Takže SELECT INTO transakce drží S uzamknout řádek v tempdb.sys.sysschobjs týkající se proměnné tabulky #A1E86130 . droptemp transakce nemůže dostat X uzamknout tento řádek kvůli konfliktnímu S zámek.

Opakovaným spuštěním tohoto dotazu zjistíte, že transaction_id pro droptemp transakce se opakovaně mění.

Domnívám se, že SQL Server musí tyto interní transakce přidělit uživatelským spidům a upřednostnit je před tím, než provede uživatelskou práci. Takže ID relace 53 uvízlo v konstantním cyklu, kdy spouští droptemp transakce, je blokována uživatelskou transakcí běžící na stejném spid. Vrátí zpět interní transakci a poté proces neomezeně opakuje.

To je potvrzeno sledováním různých událostí zamykání a transakcí v SQL Server Profiler poté, co se spid zablokuje.

Také jsem sledoval události zamykání před tím.

Zamknout blokování událostí

Většina zámků se sdíleným klíčem odstraněna pomocí SELECT INTO transakce na klíčích v sysschobjs okamžitě propustit. Výjimkou je první zámek na (246708db8c1f) .

To dává určitý smysl, protože plán ukazuje skenování vnořených smyček [sys].[sysschobjs].[clst] [o] a protože dočasným objektům je přiděleno záporné ID objektu, budou to první řádky, na které narazíte v pořadí skenování.

Také jsem se setkal se situací popsanou v OP, kdy se zdá, že spuštění třícestného křížového spojení nejprve umožňuje úspěchu čtyřcestného.

Prvních několik událostí ve trasování pro SELECT INTO transakce jsou zcela odlišné.

Stalo se to po restartu služby, takže hodnoty prostředků zámku ve sloupci textových dat nejsou přímo srovnatelné.

Zdá se, že namísto ponechání zámku na prvním klíči a následného vzoru získávání a uvolňování dalších klíčů získává mnohem více zámků, aniž by je zpočátku uvolňoval.

Předpokládám, že musí existovat určitá odchylka ve strategii provádění, která se problému vyhýbá.

Aktualizovat

Položka Connect, kterou jsem vytvořil o tom nebyl označen jako opravený, ale nyní používám SQL Server 2012 SP2 a nyní mohu reprodukovat pouze dočasné samoblokování, nikoli trvalé. Stále mám samoblokování, ale po nějakém množství neúspěšných pokusů o provedení droptemp transakce úspěšně, zdá se, že se vrací zpět ke zpracování uživatelské transakce. Poté se systémová transakce potvrdí a úspěšně se provede. Stále na stejném spidu. (proběhne osm pokusů v jednom příkladu. Nejsem si jistý, zda se to bude důsledně opakovat)



  1. Při použití více smyček foreach použijte příkaz PDO vícekrát

  2. Aby soubor database.yml fungoval v Rails na OSX

  3. MySQL Dotaz k vytažení položek, ale vždy zobrazí určitou nahoře

  4. Jak deklarovat tabulku do proměnné v MARIADB/MYSQL pro funkci/postup?