Для полной процедуры на диске TempDB требуется большая производительность?

#sql #sql-server #performance #stored-procedures #tempdb

#sql #sql-server #Производительность #хранимые процедуры #база данных tempdb

Вопрос:

следующая ситуация: у нас есть сервер Azure с базой данных, а диск с базой данных TempDB заполнен. На данный момент мы могли бы найти процесс с большими потребностями в ресурсах в activity monitor. Процесс нельзя было остановить, потому что откат не мог быть выполнен из-за переполненного диска.

Теперь у меня есть два вопроса:

  1. Может ли процесс быть причиной огромных файлов tempdb?
  2. Кто-нибудь может объяснить, почему процессу требуется так много ресурсов?

Прилагаю скриншот монитора активности и код процедуры.

Процедура запускается каждые 10 секунд с помощью агента SQL Server. Цикл внутри процедуры не такой приятный, но нам нужна была статистика, и она работала хорошо в течение последних 3 недель.

введите описание изображения здесь

 CREATE PROCEDURE Log_Blocking_Processes
AS
BEGIN
    IF OBJECT_ID('dbo.Log_Blocked_Processes', 'U') IS NULL
        BEGIN
            SELECT  GETDATE() as Tag,
                    db.name DBName,
                    tl.request_session_id,
                    wt.blocking_session_id,
                    OBJECT_NAME(p.OBJECT_ID) BlockedObjectName,
                    tl.resource_type,
                    h1.TEXT AS RequestingText,
                    h2.TEXT AS BlockingText,
                    tl.request_mode,
                    er.transaction_id as Requesting_Transaction_ID,
                    tst.transaction_id as Blocking_Transaction_ID,
                    tat.name as Blocking_Transaction_Name,
                    er.wait_time/1000 as Wait_Time_SEC
            INTO Log_Blocked_Processes
            FROM sys.dm_tran_locks AS tl
            INNER JOIN sys.databases db ON db.database_id = tl.resource_database_id
            INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address
            INNER JOIN sys.partitions AS p ON p.hobt_id = tl.resource_associated_entity_id
            INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id
            INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id
            INNER JOIN sys.dm_exec_requests er ON er.session_id = tl.request_session_id AND er.blocking_session_id = wt.blocking_session_id AND er.blocking_session_id <> 0
            INNER JOIN sys.dm_tran_session_transactions tst ON tst.session_id = wt.blocking_session_id
            INNER JOIN sys.dm_tran_active_transactions tat ON tat.transaction_id = tst.transaction_id
            CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
            CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2
            WHERE db.database_id = DB_ID();

            RETURN;
        END;

    DECLARE @DBName nvarchar(128),
            @request_session_id int,
            @blocking_session_id smallint,
            @BlockedObjectName nvarchar(128),
            @resource_type nvarchar(60),
            @RequestingText nvarchar(MAX),
            @BlockingText nvarchar(MAX),
            @request_mode nvarchar(60),
            @Requesting_Transaction_ID bigint,
            @Blocking_Transaction_ID bigint,
            @Blocking_Transaction_Name nvarchar(32),
            @Wait_Time_SEC int,
            @Tag DATE,
            @Mycount int,
            @LastInsertDay DATE,
            @counter int = 1;

    DECLARE blocked_processes_cursor CURSOR FOR   
        SELECT  cast(GETDATE() as DATE) as Tag,
                db.name DBName,
                tl.request_session_id,
                wt.blocking_session_id,
                OBJECT_NAME(p.OBJECT_ID) BlockedObjectName,
                tl.resource_type,
                h1.TEXT AS RequestingText,
                h2.TEXT AS BlockingText,
                tl.request_mode,
                er.transaction_id as Requesting_Transaction_ID,
                tst.transaction_id as Blocking_Transaction_ID,
                tat.name as Blocking_Transaction_Name,
                er.wait_time/1000 as Wait_Time_SEC
        FROM sys.dm_tran_locks AS tl
        INNER JOIN sys.databases db ON db.database_id = tl.resource_database_id
        INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address
        INNER JOIN sys.partitions AS p ON p.hobt_id = tl.resource_associated_entity_id
        INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id
        INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id
        INNER JOIN sys.dm_exec_requests er ON er.session_id = tl.request_session_id AND er.blocking_session_id = wt.blocking_session_id AND er.blocking_session_id <> 0
        INNER JOIN sys.dm_tran_session_transactions tst ON tst.session_id = wt.blocking_session_id
        INNER JOIN sys.dm_tran_active_transactions tat ON tat.transaction_id = tst.transaction_id
        CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
        CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2
        WHERE db.database_id = DB_ID();

    WHILE @counter <= 4
    BEGIN
        OPEN blocked_processes_cursor  
        FETCH NEXT FROM blocked_processes_cursor INTO @Tag, @DBName, @request_session_id, @blocking_session_id, @BlockedObjectName, @resource_type, @RequestingText, @BlockingText, @request_mode, @Requesting_Transaction_ID, @Blocking_Transaction_ID, @Blocking_Transaction_Name, @Wait_Time_SEC;

        WHILE @@FETCH_STATUS = 0  
        BEGIN
            SELECT @Mycount = count(*), @LastInsertDay = cast(max(Tag) as date) FROM Log_Blocked_Processes WHERE Requesting_Transaction_ID = @Requesting_Transaction_ID AND Blocking_Transaction_ID = @Blocking_Transaction_ID;
            IF @Mycount > 0 AND @LastInsertDay = cast(getdate() as date)
                BEGIN
                    UPDATE Log_Blocked_Processes
                    SET Wait_Time_SEC = @Wait_Time_SEC
                    WHERE Requesting_Transaction_ID = @Requesting_Transaction_ID
                    AND Blocking_Transaction_ID = @Blocking_Transaction_ID;
                END;
            ELSE
                BEGIN
                    INSERT INTO [Log_Blocked_Processes]([Tag],[DBName],[request_session_id],[blocking_session_id],[BlockedObjectName],[resource_type],[RequestingText],[BlockingText],[request_mode],[Requesting_Transaction_ID],[Blocking_Transaction_ID],[Blocking_Transaction_Name],[Wait_Time_SEC])
                    VALUES(GETDATE(),@DBName,@request_session_id,@blocking_session_id,@BlockedObjectName,@resource_type,@RequestingText,@BlockingText,@request_mode,@Requesting_Transaction_ID,@Blocking_Transaction_ID,@Blocking_Transaction_Name,@Wait_Time_SEC)
                END;

            FETCH NEXT FROM blocked_processes_cursor INTO @Tag, @DBName, @request_session_id, @blocking_session_id, @BlockedObjectName, @resource_type, @RequestingText, @BlockingText, @request_mode, @Requesting_Transaction_ID, @Blocking_Transaction_ID, @Blocking_Transaction_Name, @Wait_Time_SEC;
        END;

        SET @counter = @counter   1;
        CLOSE blocked_processes_cursor;
        WAITFOR DELAY '00:00:02';
    END;

    DEALLOCATE blocked_processes_cursor;
END;
  

Комментарии:

1. В зависимости от количества строк, процесс может привести к огромному использованию базы данных tempdb, данные вашего курсора хранятся в базе данных tempdb. RequestingText и BlockingText могут быть достаточно большими, потому что они представляют собой полные тексты хранимой процедуры, а не только одну инструкцию. Почему вы не используете расширенные события для получения blocked_process_report? Это работает как шарм.

2. Я поддерживаю расширенные события для такого рода вещей. И выработайте привычку, что когда вы начинаете думать RBAR (строка за мучительной строкой), вам нужно переосмыслить то, что вы делаете. Здесь есть вложенные циклы для обновления некоторых данных. Это можно было бы сделать с помощью одной инструкции update вместо курсора.

3. Спасибо за ваш отзыв. @DenisRubashkin звучит разумно. За последние 3 недели он регистрировал всего 3 события в день, поэтому я не ожидал, что это может вызвать проблемы. До сих пор я не знал о расширенных событиях, но я проверю это. @ Sean Lange Да, вы правы. Лучшим решением было бы одно обновление.