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サービスを再起動し、1つの接続で実行します

    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テーブルにデータを入力するクエリは、テーブル変数などの一時オブジェクトをクリーンアップする内部システムトランザクションでライブロック状態に陥ったようです。

    この方法でセッションID53をブロックすることができました。無期限にブロックされます。 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 |
    +----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
    

    リソースの説明でIDを使用する

    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は、ユーザーの作業を行う前に、これらの内部トランザクションをユーザーspidに割り当て、優先順位を付ける必要があると推測しています。したがって、セッションID 53は、droptempを起動する一定のサイクルでスタックします。 トランザクションは、同じspidで実行されているユーザートランザクションによってブロックされます。内部トランザクションをロールバックしてから、プロセスを無期限に繰り返します。

    これは、spidがハングした後、SQLServerProfilerのさまざまなロックイベントとトランザクションイベントをトレースすることで裏付けられます。

    また、その前にロックイベントを追跡しました。

    ロックイベントのブロック

    SELECT INTOによって取得された共有キーロックのほとんど sysschobjsのキーのトランザクション すぐに解放されます。例外は、(246708db8c1f)の最初のロックです。 。

    計画では、[sys].[sysschobjs].[clst] [o]のネストされたループスキャンが示されているため、これはある程度意味があります。 一時オブジェクトには負のオブジェクトIDが与えられるため、スキャン順に最初に検出される行になります。

    また、最初に3ウェイクロスジョインを実行すると、4ウェイクロスジョインが成功するように見えるというOPで説明されている状況に遭遇しました。

    SELECT INTOのトレースの最初のいくつかのイベント トランザクションにはまったく異なるパターンがあります。

    これはサービスの再起動後だったため、テキストデータ列のロックリソース値を直接比較することはできません。

    最初のキーのロックを保持してから、後続のキーを取得して解放するパターンを保持する代わりに、最初にそれらを解放せずに、より多くのロックを取得するようです。

    この問題を回避するために、実行戦略にはある程度の差異があるはずです。

    更新

    私が提起した接続アイテムこれについて 修正済みとしてマークされていませんが、現在SQL Server 2012 SP2を使用しており、永続的ではなく一時的な自己ブロックのみを再現できるようになりました。まだセルフブロッキングが発生しますが、droptempの実行に何度か失敗した後 トランザクションが正常に実行されると、ユーザートランザクションの処理に戻ったように見えます。その後、システムトランザクションがコミットされ、正常に実行されます。まだ同じスパイで。 (1つの例で8回の試行が実行されます。これが一貫して繰り返されるかどうかはわかりません)



    1. Alterコマンドを使用したOracleストアドプロシージャ

    2. C#を使用してphpスクリプトを呼び出す(Unity)

    3. 登録ジョブレベルでのSpringBatchジョブのトランザクションサポート

    4. Oracleでカレンダーテーブルを作成する方法