Monitorowanie wydajności usługi Microsoft Azure SQL Database przy użyciu dynamicznych widoków zarządzania

Dotyczy:Azure SQL Database

Usługa Microsoft Azure SQL Database umożliwia podzestaw dynamicznych widoków zarządzania do diagnozowania problemów z wydajnością, które mogą być spowodowane przez zablokowane lub długotrwałe zapytania, wąskie gardła zasobów, słabe plany zapytań i inne.

Ten artykuł zawiera informacje na temat wykrywania typowych problemów z wydajnością przez wykonywanie zapytań dotyczących dynamicznych widoków zarządzania za pośrednictwem języka T-SQL. Możesz użyć dowolnego narzędzia do wykonywania zapytań, takiego jak:

Uprawnienia

W usłudze Azure SQL Database, w zależności od rozmiaru obliczeniowego i opcji wdrożenia, wykonywanie zapytań względem dynamicznego widoku widoku może wymagać uprawnienia WYŚWIETL STAN BAZY DANYCH lub WYŚWIETL STAN SERWERA. Drugie uprawnienie może zostać przyznane za pośrednictwem członkostwa w ##MS_ServerStateReader## roli serwera.

Aby udzielić uprawnienia WYŚWIETL STAN BAZY danych dla określonego użytkownika bazy danych, uruchom następujące zapytanie jako przykład:

GRANT VIEW DATABASE STATE TO database_user;

Aby przyznać członkostwu ##MS_ServerStateReader## roli serwera do logowania dla serwera logicznego na platformie Azure, połącz się z master bazą danych, a następnie uruchom następujące zapytanie jako przykład:

ALTER SERVER ROLE [##MS_ServerStateReader##] ADD MEMBER [login];

W wystąpieniu programu SQL Server i w usłudze Azure SQL Managed Instance dynamiczne widoki zarządzania zwracają informacje o stanie serwera. W usłudze Azure SQL Database zwracają informacje dotyczące bieżącej logicznej bazy danych.

Identyfikowanie problemów z wydajnością procesora CPU

Jeśli użycie procesora CPU przekracza 80% przez dłuższy czas, rozważ następujące kroki rozwiązywania problemów, czy problem z procesorem CPU występuje teraz , czy wystąpił w przeszłości.

Problem z procesorem CPU występuje teraz

Jeśli problem występuje teraz, istnieją dwa możliwe scenariusze:

Wiele pojedynczych zapytań, które zbiorczo zużywają wysokie użycie procesora CPU
  • Użyj następującego zapytania, aby zidentyfikować najważniejsze skróty zapytań:

    PRINT '-- top 10 Active CPU Consuming Queries (aggregated)--';
    SELECT TOP 10 GETDATE() runtime, *
    FROM (SELECT query_stats.query_hash, SUM(query_stats.cpu_time) 'Total_Request_Cpu_Time_Ms', SUM(logical_reads) 'Total_Request_Logical_Reads', MIN(start_time) 'Earliest_Request_start_Time', COUNT(*) 'Number_Of_Requests', SUBSTRING(REPLACE(REPLACE(MIN(query_stats.statement_text), CHAR(10), ' '), CHAR(13), ' '), 1, 256) AS "Statement_Text"
        FROM (SELECT req.*, SUBSTRING(ST.text, (req.statement_start_offset / 2)+1, ((CASE statement_end_offset WHEN -1 THEN DATALENGTH(ST.text)ELSE req.statement_end_offset END-req.statement_start_offset)/ 2)+1) AS statement_text
              FROM sys.dm_exec_requests AS req
                    CROSS APPLY sys.dm_exec_sql_text(req.sql_handle) AS ST ) AS query_stats
        GROUP BY query_hash) AS t
    ORDER BY Total_Request_Cpu_Time_Ms DESC;
    
Długotrwałe zapytania, które zużywają procesor CPU, są nadal uruchomione
  • Użyj następującego zapytania, aby zidentyfikować te zapytania:

    PRINT '--top 10 Active CPU Consuming Queries by sessions--';
    SELECT TOP 10 req.session_id, req.start_time, cpu_time 'cpu_time_ms', OBJECT_NAME(ST.objectid, ST.dbid) 'ObjectName', SUBSTRING(REPLACE(REPLACE(SUBSTRING(ST.text, (req.statement_start_offset / 2)+1, ((CASE statement_end_offset WHEN -1 THEN DATALENGTH(ST.text)ELSE req.statement_end_offset END-req.statement_start_offset)/ 2)+1), CHAR(10), ' '), CHAR(13), ' '), 1, 512) AS statement_text
    FROM sys.dm_exec_requests AS req
        CROSS APPLY sys.dm_exec_sql_text(req.sql_handle) AS ST
    ORDER BY cpu_time DESC;
    GO
    

Problem z procesorem CPU wystąpił w przeszłości

Jeśli problem wystąpił w przeszłości i chcesz przeprowadzić analizę głównej przyczyny, użyj magazynu zapytań. Użytkownicy z dostępem do bazy danych mogą używać języka T-SQL do wykonywania zapytań dotyczących danych magazynu zapytań. Domyślne konfiguracje magazynu zapytań używają stopnia szczegółowości 1 godziny.

  1. Użyj następującego zapytania, aby przyjrzeć się aktywności w przypadku zapytań zużywających wysokie użycie procesora CPU. To zapytanie zwraca 15 najważniejszych zapytań zużywających procesor CPU. Pamiętaj, aby zmienić :rsi.start_time >= DATEADD(hour, -2, GETUTCDATE()

    -- Top 15 CPU consuming queries by query hash
    -- Note that a query hash can have many query ids if not parameterized or not parameterized properly
    WITH AggregatedCPU
    AS (
        SELECT q.query_hash
            ,SUM(count_executions * avg_cpu_time / 1000.0) AS total_cpu_ms
            ,SUM(count_executions * avg_cpu_time / 1000.0) / SUM(count_executions) AS avg_cpu_ms
            ,MAX(rs.max_cpu_time / 1000.00) AS max_cpu_ms
            ,MAX(max_logical_io_reads) max_logical_reads
            ,COUNT(DISTINCT p.plan_id) AS number_of_distinct_plans
            ,COUNT(DISTINCT p.query_id) AS number_of_distinct_query_ids
            ,SUM(CASE 
                    WHEN rs.execution_type_desc = 'Aborted'
                        THEN count_executions
                    ELSE 0
                    END) AS Aborted_Execution_Count
            ,SUM(CASE 
                    WHEN rs.execution_type_desc = 'Regular'
                        THEN count_executions
                    ELSE 0
                    END) AS Regular_Execution_Count
            ,SUM(CASE 
                    WHEN rs.execution_type_desc = 'Exception'
                        THEN count_executions
                    ELSE 0
                    END) AS Exception_Execution_Count
            ,SUM(count_executions) AS total_executions
            ,MIN(qt.query_sql_text) AS sampled_query_text
        FROM sys.query_store_query_text AS qt
            INNER JOIN sys.query_store_query AS q ON qt.query_text_id = q.query_text_id
            INNER JOIN sys.query_store_plan AS p ON q.query_id = p.query_id
            INNER JOIN sys.query_store_runtime_stats AS rs ON rs.plan_id = p.plan_id
            INNER JOIN sys.query_store_runtime_stats_interval AS rsi ON rsi.runtime_stats_interval_id = rs.runtime_stats_interval_id
        WHERE rs.execution_type_desc IN ('Regular','Aborted','Exception')
            AND rsi.start_time >= DATEADD(HOUR, - 2, GETUTCDATE())
        GROUP BY q.query_hash
        )
        ,OrderedCPU
    AS (
        SELECT query_hash
            ,total_cpu_ms
            ,avg_cpu_ms
            ,max_cpu_ms
            ,max_logical_reads
            ,number_of_distinct_plans
            ,number_of_distinct_query_ids
            ,total_executions
            ,Aborted_Execution_Count
            ,Regular_Execution_Count
            ,Exception_Execution_Count
            ,sampled_query_text
            ,ROW_NUMBER() OVER (
                ORDER BY total_cpu_ms DESC
                    ,query_hash ASC
                ) AS query_hash_row_number
        FROM AggregatedCPU
        )
    SELECT OD.query_hash
        ,OD.total_cpu_ms
        ,OD.avg_cpu_ms
        ,OD.max_cpu_ms
        ,OD.max_logical_reads
        ,OD.number_of_distinct_plans
        ,OD.number_of_distinct_query_ids
        ,OD.total_executions
        ,OD.Aborted_Execution_Count
        ,OD.Regular_Execution_Count
        ,OD.Exception_Execution_Count
        ,OD.sampled_query_text
        ,OD.query_hash_row_number
    FROM OrderedCPU AS OD
    WHERE OD.query_hash_row_number <= 15 --get top 15 rows by total_cpu_ms
    ORDER BY total_cpu_ms DESC;
    
  2. Po zidentyfikowaniu problematycznych zapytań nadszedł czas, aby dostosować te zapytania w celu zmniejszenia wykorzystania procesora CPU. Jeśli nie masz czasu na dostosowanie zapytań, możesz również zdecydować się na uaktualnienie celu slo bazy danych, aby obejść ten problem.

Aby uzyskać więcej informacji na temat obsługi problemów z wydajnością procesora CPU w usłudze Azure SQL Database, zobacz Diagnozowanie i rozwiązywanie problemów z wysokim użyciem procesora CPU w usłudze Azure SQL Database.

Zidentyfikuj problemy z wydajnością operacji We/Wy

Podczas identyfikowania problemów z wydajnością wejścia/wyjścia magazynu (we/wy) najważniejsze typy oczekiwania skojarzone z problemami we/wy to:

  • PAGEIOLATCH_*

    W przypadku problemów z we/wy pliku danych (w tym PAGEIOLATCH_SH, PAGEIOLATCH_EX, PAGEIOLATCH_UP). Jeśli nazwa typu oczekiwania zawiera we/wy, wskazuje na problem z we/wy. Jeśli w nazwie oczekiwania na zatrzask strony nie ma operacji we/wy, wskazuje na inny typ problemu (na przykład tempdb rywalizację).

  • WRITE_LOG

    W przypadku problemów z we/wy dziennika transakcji.

Jeśli problem we/wy występuje teraz

Użyj sys.dm_exec_requests lub sys.dm_os_waiting_tasks, aby wyświetlić element wait_type i wait_time.

Identyfikowanie użycia operacji we/wy danych i dzienników

Użyj następującego zapytania, aby zidentyfikować dane i użycie operacji we/wy dziennika. Jeśli dane lub we/wy dziennika są wyższe niż 80%, oznacza to, że użytkownicy korzystali z dostępnej operacji we/wy dla warstwy usługi Azure SQL Database.

SELECT
    database_name = DB_NAME()
,   UTC_time = end_time
,   'CPU Utilization In % of Limit'           = rs.avg_cpu_percent
,   'Data IO In % of Limit'                   = rs.avg_data_io_percent
,   'Log Write Utilization In % of Limit'     = rs.avg_log_write_percent
,   'Memory Usage In % of Limit'              = rs.avg_memory_usage_percent 
,   'In-Memory OLTP Storage in % of Limit'    = rs.xtp_storage_percent
,   'Concurrent Worker Threads in % of Limit' = rs.max_worker_percent
,   'Concurrent Sessions in % of Limit'       = rs.max_session_percent
FROM sys.dm_db_resource_stats AS rs  --past hour only
ORDER BY  rs.end_time DESC;

Aby uzyskać więcej przykładów użycia sys.dm_db_resource_stats, zobacz sekcję Monitorowanie użycia zasobów w dalszej części tego artykułu.

Jeśli osiągnięto limit operacji we/wy, masz dwie opcje:

  • Uaktualnij rozmiar obliczeniowy lub warstwę usługi
  • Identyfikowanie i dostrajanie zapytań zużywających najwięcej operacji we/wy.

W przypadku opcji 2 można użyć następującego zapytania względem magazynu zapytań dla operacji we/wy związanych z buforem, aby wyświetlić ostatnie dwie godziny śledzonych działań:

-- Top queries that waited on buffer
-- Note these are finished queries
WITH Aggregated AS (SELECT q.query_hash, SUM(total_query_wait_time_ms) total_wait_time_ms, SUM(total_query_wait_time_ms / avg_query_wait_time_ms) AS total_executions, MIN(qt.query_sql_text) AS sampled_query_text, MIN(wait_category_desc) AS wait_category_desc
                    FROM sys.query_store_query_text AS qt
                         INNER JOIN sys.query_store_query AS q ON qt.query_text_id=q.query_text_id
                         INNER JOIN sys.query_store_plan AS p ON q.query_id=p.query_id
                         INNER JOIN sys.query_store_wait_stats AS waits ON waits.plan_id=p.plan_id
                         INNER JOIN sys.query_store_runtime_stats_interval AS rsi ON rsi.runtime_stats_interval_id=waits.runtime_stats_interval_id
                    WHERE wait_category_desc='Buffer IO' AND rsi.start_time>=DATEADD(HOUR, -2, GETUTCDATE())
                    GROUP BY q.query_hash), Ordered AS (SELECT query_hash, total_executions, total_wait_time_ms, sampled_query_text, wait_category_desc, ROW_NUMBER() OVER (ORDER BY total_wait_time_ms DESC, query_hash ASC) AS query_hash_row_number
                                                        FROM Aggregated)
SELECT OD.query_hash, OD.total_executions, OD.total_wait_time_ms, OD.sampled_query_text, OD.wait_category_desc, OD.query_hash_row_number
FROM Ordered AS OD
WHERE OD.query_hash_row_number <= 15 -- get top 15 rows by total_wait_time_ms
ORDER BY total_wait_time_ms DESC;
GO
Wyświetlanie całkowitej operacji we/wy dziennika na potrzeby oczekiwania funkcji WRITELOG

Jeśli typ oczekiwania to WRITELOG, użyj następującego zapytania, aby wyświetlić łączną liczbę operacji we/wy dziennika według instrukcji:

-- Top transaction log consumers
-- Adjust the time window by changing
-- rsi.start_time >= DATEADD(hour, -2, GETUTCDATE())
WITH AggregatedLogUsed
AS (SELECT q.query_hash,
           SUM(count_executions * avg_cpu_time / 1000.0) AS total_cpu_ms,
           SUM(count_executions * avg_cpu_time / 1000.0) / SUM(count_executions) AS avg_cpu_ms,
           SUM(count_executions * avg_log_bytes_used) AS total_log_bytes_used,
           MAX(rs.max_cpu_time / 1000.00) AS max_cpu_ms,
           MAX(max_logical_io_reads) max_logical_reads,
           COUNT(DISTINCT p.plan_id) AS number_of_distinct_plans,
           COUNT(DISTINCT p.query_id) AS number_of_distinct_query_ids,
           SUM(   CASE
                      WHEN rs.execution_type_desc = 'Aborted' THEN
                          count_executions
                      ELSE 0
                  END
              ) AS Aborted_Execution_Count,
           SUM(   CASE
                      WHEN rs.execution_type_desc = 'Regular' THEN
                          count_executions
                      ELSE 0
                  END
              ) AS Regular_Execution_Count,
           SUM(   CASE
                      WHEN rs.execution_type_desc = 'Exception' THEN
                          count_executions
                      ELSE 0
                  END
              ) AS Exception_Execution_Count,
           SUM(count_executions) AS total_executions,
           MIN(qt.query_sql_text) AS sampled_query_text
    FROM sys.query_store_query_text AS qt
        INNER JOIN sys.query_store_query AS q ON qt.query_text_id = q.query_text_id
        INNER JOIN sys.query_store_plan AS p ON q.query_id = p.query_id
        INNER JOIN sys.query_store_runtime_stats AS rs ON rs.plan_id = p.plan_id
        INNER JOIN sys.query_store_runtime_stats_interval AS rsi ON rsi.runtime_stats_interval_id = rs.runtime_stats_interval_id
    WHERE rs.execution_type_desc IN ( 'Regular', 'Aborted', 'Exception' )
          AND rsi.start_time >= DATEADD(HOUR, -2, GETUTCDATE())
    GROUP BY q.query_hash),
     OrderedLogUsed
AS (SELECT query_hash,
           total_log_bytes_used,
           number_of_distinct_plans,
           number_of_distinct_query_ids,
           total_executions,
           Aborted_Execution_Count,
           Regular_Execution_Count,
           Exception_Execution_Count,
           sampled_query_text,
           ROW_NUMBER() OVER (ORDER BY total_log_bytes_used DESC, query_hash ASC) AS query_hash_row_number
    FROM AggregatedLogUsed)
SELECT OD.total_log_bytes_used,
       OD.number_of_distinct_plans,
       OD.number_of_distinct_query_ids,
       OD.total_executions,
       OD.Aborted_Execution_Count,
       OD.Regular_Execution_Count,
       OD.Exception_Execution_Count,
       OD.sampled_query_text,
       OD.query_hash_row_number
FROM OrderedLogUsed AS OD
WHERE OD.query_hash_row_number <= 15 -- get top 15 rows by total_log_bytes_used
ORDER BY total_log_bytes_used DESC;
GO

Identyfikowanie problemów z wydajnością bazy danych tempdb

Podczas identyfikowania problemów z wydajnością we/wy najważniejsze typy oczekiwania skojarzone z problemami tempdb to PAGELATCH_* (nie PAGEIOLATCH_*). Jednak oczekiwania nie zawsze oznaczają, PAGELATCH_* że masz tempdb rywalizację. To oczekiwanie może również oznaczać występowanie rywalizacji o stronę danych obiektu użytkownika z powodu współbieżnych żądań przeznaczonych dla tej samej strony danych. Aby jeszcze bardziej potwierdzić tempdb rywalizację, użyj sys.dm_exec_requests , aby potwierdzić, że wait_resource wartość zaczyna się od 2:x:y miejsca, gdzie 2 jest tempdb identyfikatorem bazy danych, x jest identyfikatorem pliku i y jest identyfikatorem strony.

W przypadku tempdb rywalizacji typową metodą jest zmniejszenie lub przepisanie kodu aplikacji, który opiera się na metodzie tempdb. Typowe tempdb obszary użycia obejmują:

  • Tabele tymczasowe
  • Zmienne tabeli
  • Parametry z wartościami przechowywanymi w tabeli
  • Użycie magazynu wersji (skojarzone z długotrwałymi transakcjami)
  • Zapytania z planami, które używają sortowania, sprzężeń skrótów i buforów

Aby uzyskać więcej informacji, zobacz tempdb in Azure SQL (Baza danych tempdb w usłudze Azure SQL).

Najważniejsze zapytania korzystające ze zmiennych tabeli i tabel tymczasowych

Użyj następującego zapytania, aby zidentyfikować najważniejsze zapytania korzystające ze zmiennych tabeli i tabel tymczasowych:

SELECT plan_handle, execution_count, query_plan
INTO #tmpPlan
FROM sys.dm_exec_query_stats
     CROSS APPLY sys.dm_exec_query_plan(plan_handle);
GO

WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS sp)
SELECT plan_handle, stmt.stmt_details.value('@Database', 'varchar(max)') AS 'Database'
, stmt.stmt_details.value('@Schema', 'varchar(max)') AS 'Schema'
, stmt.stmt_details.value('@Table', 'varchar(max)') AS 'table'
INTO #tmp2
FROM
    (SELECT CAST(query_plan AS XML) sqlplan, plan_handle FROM #tmpPlan) AS p
        CROSS APPLY sqlplan.nodes('//sp:Object') AS stmt(stmt_details);
GO

SELECT t.plan_handle, [Database], [Schema], [table], execution_count
FROM
    (SELECT DISTINCT plan_handle, [Database], [Schema], [table]
     FROM #tmp2
     WHERE [table] LIKE '%@%' OR [table] LIKE '%#%') AS t
        INNER JOIN #tmpPlan AS t2 ON t.plan_handle=t2.plan_handle;
GO
DROP TABLE #tmpPlan
DROP TABLE #tmp2

Identyfikowanie długotrwałych transakcji

Użyj następującego zapytania, aby zidentyfikować długotrwałe transakcje. Długotrwałe transakcje uniemożliwiają czyszczenie trwałego magazynu wersji (PVS). Aby uzyskać więcej informacji, zobacz Rozwiązywanie problemów z przyspieszonym odzyskiwaniem bazy danych.

SELECT DB_NAME(dtr.database_id) 'database_name',
       sess.session_id,
       atr.name AS 'tran_name',
       atr.transaction_id,
       transaction_type,
       transaction_begin_time,
       database_transaction_begin_time, 
       transaction_state,
       is_user_transaction,
       sess.open_transaction_count,
       TRIM(REPLACE(
                REPLACE(
                            SUBSTRING(
                                        SUBSTRING(
                                                    txt.text,
                                                    (req.statement_start_offset / 2) + 1,
                                                    ((CASE req.statement_end_offset
                                                            WHEN -1 THEN
                                                                DATALENGTH(txt.text)
                                                            ELSE
                                                                req.statement_end_offset
                                                        END - req.statement_start_offset
                                                    ) / 2
                                                    ) + 1
                                                ),
                                        1,
                                        1000
                                    ),
                            CHAR(10),
                            ' '
                        ),
                CHAR(13),
                ' '
            )
            ) Running_stmt_text,
       recenttxt.text 'MostRecentSQLText'
FROM sys.dm_tran_active_transactions AS atr
    INNER JOIN sys.dm_tran_database_transactions AS dtr
        ON dtr.transaction_id = atr.transaction_id
    LEFT JOIN sys.dm_tran_session_transactions AS sess
        ON sess.transaction_id = atr.transaction_id
    LEFT JOIN sys.dm_exec_requests AS req
        ON req.session_id = sess.session_id
           AND req.transaction_id = sess.transaction_id
    LEFT JOIN sys.dm_exec_connections AS conn
        ON sess.session_id = conn.session_id
    OUTER APPLY sys.dm_exec_sql_text(req.sql_handle) AS txt
    OUTER APPLY sys.dm_exec_sql_text(conn.most_recent_sql_handle) AS recenttxt
WHERE atr.transaction_type != 2
      AND sess.session_id != @@spid
ORDER BY start_time ASC;

Identyfikowanie problemów z wydajnością oczekiwania na udzielanie pamięci

Jeśli twój typ oczekiwania jest RESOURCE_SEMAPHORE najwyższy i nie masz problemu z wysokim użyciem procesora CPU, może wystąpić problem z oczekiwaniem na udzielenie pamięci.

Ustal, czy oczekiwanie RESOURCE_SEMAPHORE jest oczekiwaniem na górę

Użyj następującego zapytania, aby określić, czy RESOURCE_SEMAPHORE oczekiwanie jest najważniejsze. Wskazuje również, że rosnąca ranga czasu oczekiwania w najnowszej RESOURCE_SEMAPHORE historii. Aby uzyskać więcej informacji na temat rozwiązywania problemów z oczekiwaniem na udzielanie pamięci, zobacz Rozwiązywanie problemów z niską wydajnością lub małą ilością pamięci spowodowanych przez przydziały pamięci w programie SQL Server.

SELECT wait_type,
       SUM(wait_time) AS total_wait_time_ms
FROM sys.dm_exec_requests AS req
    INNER JOIN sys.dm_exec_sessions AS sess
        ON req.session_id = sess.session_id
WHERE is_user_process = 1
GROUP BY wait_type
ORDER BY SUM(wait_time) DESC;
Identyfikowanie instrukcji zużywających dużą ilość pamięci

Jeśli wystąpią błędy braku pamięci w usłudze Azure SQL Database, zapoznaj się z sys.dm_os_out_of_memory_events. Aby uzyskać więcej informacji, zobacz Rozwiązywanie problemów z błędami braku pamięci w usłudze Azure SQL Database.

Najpierw zmodyfikuj następujący skrypt, aby zaktualizować odpowiednie wartości i start_timeend_time. Następnie uruchom następujące zapytanie, aby zidentyfikować instrukcje zużywające dużą ilość pamięci:

SELECT IDENTITY(INT, 1, 1) rowId,
    CAST(query_plan AS XML) query_plan,
    p.query_id
INTO #tmp
FROM sys.query_store_plan AS p
    INNER JOIN sys.query_store_runtime_stats AS r
        ON p.plan_id = r.plan_id
    INNER JOIN sys.query_store_runtime_stats_interval AS i
        ON r.runtime_stats_interval_id = i.runtime_stats_interval_id
WHERE start_time > '2018-10-11 14:00:00.0000000'
      AND end_time < '2018-10-17 20:00:00.0000000';
GO
;WITH cte
AS (SELECT query_id,
        query_plan,
        m.c.value('@SerialDesiredMemory', 'INT') AS SerialDesiredMemory
    FROM #tmp AS t
        CROSS APPLY t.query_plan.nodes('//*:MemoryGrantInfo[@SerialDesiredMemory[. > 0]]') AS m(c) )
SELECT TOP 50
    cte.query_id,
    t.query_sql_text,
    cte.query_plan,
    CAST(SerialDesiredMemory / 1024. AS DECIMAL(10, 2)) SerialDesiredMemory_MB
FROM cte
    INNER JOIN sys.query_store_query AS q
        ON cte.query_id = q.query_id
    INNER JOIN sys.query_store_query_text AS t
        ON q.query_text_id = t.query_text_id
ORDER BY SerialDesiredMemory DESC;
Identyfikowanie 10 aktywnych przydziałów pamięci

Użyj następującego zapytania, aby zidentyfikować 10 aktywnych przydziałów pamięci:

SELECT TOP 10
    CONVERT(VARCHAR(30), GETDATE(), 121) AS runtime,
       r.session_id,
       r.blocking_session_id,
       r.cpu_time,
       r.total_elapsed_time,
       r.reads,
       r.writes,
       r.logical_reads,
       r.row_count,
       wait_time,
       wait_type,
       r.command,
       OBJECT_NAME(txt.objectid, txt.dbid) 'Object_Name',
       TRIM(REPLACE(REPLACE(SUBSTRING(SUBSTRING(TEXT, (r.statement_start_offset / 2) + 1, 
        (  (
            CASE r.statement_end_offset
                WHEN - 1
                    THEN DATALENGTH(TEXT)
                ELSE r.statement_end_offset
                END - r.statement_start_offset
            ) / 2
        ) + 1), 1, 1000), CHAR(10), ' '), CHAR(13), ' ')) AS stmt_text,
       mg.dop,                                               --Degree of parallelism
       mg.request_time,                                      --Date and time when this query requested the memory grant.
       mg.grant_time,                                        --NULL means memory has not been granted
       mg.requested_memory_kb / 1024.0 requested_memory_mb,  --Total requested amount of memory in megabytes
       mg.granted_memory_kb / 1024.0 AS granted_memory_mb,   --Total amount of memory actually granted in megabytes. NULL if not granted
       mg.required_memory_kb / 1024.0 AS required_memory_mb, --Minimum memory required to run this query in megabytes.
       max_used_memory_kb / 1024.0 AS max_used_memory_mb,
       mg.query_cost,                                        --Estimated query cost.
       mg.timeout_sec,                                       --Time-out in seconds before this query gives up the memory grant request.
       mg.resource_semaphore_id,                             --Non-unique ID of the resource semaphore on which this query is waiting.
       mg.wait_time_ms,                                      --Wait time in milliseconds. NULL if the memory is already granted.
       CASE mg.is_next_candidate --Is this process the next candidate for a memory grant
           WHEN 1 THEN
               'Yes'
           WHEN 0 THEN
               'No'
           ELSE
               'Memory has been granted'
       END AS 'Next Candidate for Memory Grant',
       qp.query_plan
FROM sys.dm_exec_requests AS r
    INNER JOIN sys.dm_exec_query_memory_grants AS mg
        ON r.session_id = mg.session_id
           AND r.request_id = mg.request_id
    CROSS APPLY sys.dm_exec_sql_text(mg.sql_handle) AS txt
    CROSS APPLY sys.dm_exec_query_plan(r.plan_handle) AS qp
ORDER BY mg.granted_memory_kb DESC;

Monitorowanie połączeń

Możesz użyć widoku sys.dm_exec_connections , aby pobrać informacje o połączeniach ustanowionych z określoną bazą danych lub elastyczną pulą oraz szczegóły każdego połączenia. Ponadto widok sys.dm_exec_sessions jest przydatny podczas pobierania informacji o wszystkich aktywnych połączeniach użytkowników i zadaniach wewnętrznych.

Wyświetlanie bieżących sesji

Poniższe zapytanie pobiera informacje dotyczące bieżącego połączenia. Aby wyświetlić wszystkie sesje, usuń klauzulę WHERE .

Podczas wykonywania sys.dm_exec_requests widoków i sys.dm_exec_sessions są widoczne wszystkie sesje wykonywane w bazie danych tylko wtedy, gdy masz uprawnienie WYŚWIETL STAN BAZY danych w bazie danych. W przeciwnym razie zobaczysz tylko bieżącą sesję.

SELECT
    c.session_id, c.net_transport, c.encrypt_option,
    c.auth_scheme, s.host_name, s.program_name,
    s.client_interface_name, s.login_name, s.nt_domain,
    s.nt_user_name, s.original_login_name, c.connect_time,
    s.login_time
FROM sys.dm_exec_connections AS c
    INNER JOIN sys.dm_exec_sessions AS s
        ON c.session_id = s.session_id
WHERE c.session_id = @@SPID; --Remove to view all sessions, if permissions allow

Monitorowanie użycia zasobów

Użycie zasobów usługi Azure SQL Database można monitorować na poziomie zapytania przy użyciu szczegółowych informacji o wydajności zapytań usługi SQL Database w witrynie Azure Portal lub magazynie zapytań.

Możesz również monitorować użycie przy użyciu następujących widoków:

sys.dm_db_resource_stats

Widok sys.dm_db_resource_stats można używać w każdej bazie danych. Widok sys.dm_db_resource_stats pokazuje ostatnie dane użycia zasobów względem warstwy usługi. Średnie wartości procentowe procesora CPU, operacji we/wy danych, zapisów dzienników i pamięci są rejestrowane co 15 sekund i są przechowywane przez 1 godzinę.

Ponieważ ten widok zapewnia bardziej szczegółowe spojrzenie na użycie zasobów, należy najpierw użyć sys.dm_db_resource_stats funkcji analizy bieżącego stanu lub rozwiązywania problemów. Na przykład to zapytanie pokazuje średnie i maksymalne użycie zasobów dla bieżącej bazy danych w ciągu ostatniej godziny:

SELECT
    Database_Name = DB_NAME(),
    tier_limit = COALESCE(rs.dtu_limit, cpu_limit), --DTU or vCore limit
    AVG(avg_cpu_percent) AS 'Average CPU use in percent',
    MAX(avg_cpu_percent) AS 'Maximum CPU use in percent',
    AVG(avg_data_io_percent) AS 'Average data IO in percent',
    MAX(avg_data_io_percent) AS 'Maximum data IO in percent',
    AVG(avg_log_write_percent) AS 'Average log write use in percent',
    MAX(avg_log_write_percent) AS 'Maximum log write use in percent',
    AVG(avg_memory_usage_percent) AS 'Average memory use in percent',
    MAX(avg_memory_usage_percent) AS 'Maximum memory use in percent'
FROM sys.dm_db_resource_stats AS rs --past hour only
GROUP BY rs.dtu_limit, rs.cpu_limit;  

Inne zapytania można znaleźć w przykładach w sys.dm_db_resource_stats.

sys.resource_stats

Widok sys.resource_stats w master bazie danych zawiera dodatkowe informacje, które mogą ułatwić monitorowanie wydajności bazy danych w określonej warstwie usługi i rozmiarze obliczeniowym. Dane są zbierane co 5 minut i są przechowywane przez około 14 dni. Ten widok jest przydatny w przypadku długoterminowej analizy historycznej sposobu, w jaki baza danych korzysta z zasobów.

Na poniższym wykresie przedstawiono użycie zasobów procesora CPU dla bazy danych Premium z rozmiarem obliczeniowym P2 dla każdej godziny w tygodniu. Ten wykres rozpoczyna się w poniedziałek, pokazuje pięć dni roboczych, a następnie pokazuje weekend, gdy w aplikacji dzieje się znacznie mniej.

Zrzut ekranu przedstawiający przykładowy graf użycia zasobów bazy danych.

Z danych ta baza danych ma obecnie szczytowe obciążenie procesora CPU nieco ponad 50 procent użycia względem rozmiaru obliczeniowego P2 (w południe we wtorek). Jeśli procesor CPU jest dominującym czynnikiem w profilu zasobu aplikacji, możesz zdecydować, że P2 jest odpowiednim rozmiarem obliczeniowym, aby zagwarantować, że obciążenie zawsze pasuje. Jeśli spodziewasz się, że aplikacja wzrośnie wraz z upływem czasu, dobrym pomysłem jest posiadanie dodatkowego buforu zasobów, aby aplikacja nigdy nie osiągnęła limitu poziomu wydajności. Jeśli zwiększysz rozmiar obliczeniowy, możesz uniknąć widocznych przez klienta błędów, które mogą wystąpić, gdy baza danych nie ma wystarczającej mocy do efektywnego przetwarzania żądań, szczególnie w środowiskach wrażliwych na opóźnienia. Przykładem jest baza danych, która obsługuje aplikację, która maluje strony internetowe na podstawie wyników wywołań bazy danych.

Inne typy aplikacji mogą interpretować ten sam graf inaczej. Jeśli na przykład aplikacja próbuje przetwarzać dane listy płac każdego dnia i ma ten sam wykres, ten rodzaj modelu "zadania wsadowego" może być odpowiedni w przypadku rozmiaru obliczeniowego P1. Rozmiar obliczeniowy P1 ma 100 jednostek DTU w porównaniu z 200 jednostkami DTU w rozmiarze obliczeniowym P2. Rozmiar obliczeniowy P1 zapewnia połowę wydajności rozmiaru obliczeniowego P2. Dlatego 50 procent użycia procesora CPU w P2 jest równe 100 procent użycia procesora w P1. Jeśli aplikacja nie ma limitów czasu, może nie mieć znaczenia, czy zadanie trwa 2 godziny lub 2,5 godziny, jeśli zostanie wykonane dzisiaj. Aplikacja w tej kategorii prawdopodobnie może używać rozmiaru obliczeniowego P1. Możesz skorzystać z faktu, że istnieją okresy czasu w ciągu dnia, gdy użycie zasobów jest niższe, dzięki czemu każdy "duży szczyt" może rozlać się do jednego z korytów w późniejszym terminie. Rozmiar obliczeniowy P1 może być odpowiedni dla tego rodzaju aplikacji (i zaoszczędzić pieniądze), o ile zadania mogą być wykonywane codziennie.

Aparat bazy danych uwidacznia informacje o zużytych zasobach dla każdej aktywnej master bazy danych w sys.resource_stats widoku bazy danych na każdym serwerze. Dane w tabeli są agregowane dla 5-minutowych interwałów. W przypadku warstw usług Podstawowa, Standardowa i Premium dane mogą pojawić się w tabeli dłużej niż 5 minut, więc te dane są bardziej przydatne w przypadku analizy historycznej, a nie analizy niemal w czasie rzeczywistym. sys.resource_stats Wykonaj zapytanie względem widoku, aby wyświetlić najnowszą historię bazy danych i sprawdzić, czy rezerwacja została wybrana w celu dostarczenia żądanej wydajności w razie potrzeby.

Uwaga

W usłudze Azure SQL Database musisz nawiązać połączenie z bazą master danych, aby wykonać zapytanie sys.resource_stats w poniższych przykładach.

W tym przykładzie pokazano, jak dane w tym widoku są uwidocznione:

SELECT TOP 10 *
FROM sys.resource_stats
WHERE database_name = 'userdb1'
ORDER BY start_time DESC;

W następnym przykładzie przedstawiono różne sposoby używania sys.resource_stats widoku wykazu w celu uzyskania informacji o sposobie korzystania z zasobów bazy danych:

  1. Aby zapoznać się z użyciem zasobu z ostatniego tygodnia dla bazy danych userdb1użytkownika , możesz uruchomić to zapytanie, zastępując własną nazwę bazy danych:

    SELECT *
    FROM sys.resource_stats
    WHERE database_name = 'userdb1' 
        AND start_time > DATEADD(day, -7, GETDATE())
    ORDER BY start_time DESC;
    
  2. Aby ocenić, na ile Twoje obciążenie pasuje do określonego rozmiaru obliczeniowego, sprawdź szczegóły wszystkich aspektów metryk zasobów: procesor CPU, odczyty, zapisy, liczba procesów roboczych i liczba sesji. Oto poprawione zapytanie używane do sys.resource_stats raportowania średnich i maksymalnych wartości tych metryk zasobów dla każdej warstwy usługi, dla których baza danych została aprowizowana dla:

    SELECT rs.database_name
    ,    rs.sku
    ,    storage_mb                           = MAX(rs.Storage_in_megabytes)
    ,    'Average CPU Utilization In %'       = AVG(rs.avg_cpu_percent)            
    ,    'Maximum CPU Utilization In %'       = MAX(rs.avg_cpu_percent)            
    ,    'Average Data IO In %'               = AVG(rs.avg_data_io_percent)        
    ,    'Maximum Data IO In %'               = MAX(rs.avg_data_io_percent)        
    ,    'Average Log Write Utilization In %' = AVG(rs.avg_log_write_percent)           
    ,    'Maximum Log Write Utilization In %' = MAX(rs.avg_log_write_percent)           
    ,    'Average Requests In %'              = AVG(rs.max_worker_percent)    
    ,    'Maximum Requests In %'              = MAX(rs.max_worker_percent)    
    ,    'Average Sessions In %'              = AVG(rs.max_session_percent)    
    ,    'Maximum Sessions In %'              = MAX(rs.max_session_percent)    
    FROM sys.resource_stats AS rs
    WHERE rs.database_name = 'userdb1' 
    AND rs.start_time > DATEADD(day, -7, GETDATE())
    GROUP BY rs.database_name, rs.sku;
    
  3. Dzięki tym informacjom o średnich i maksymalnych wartościach każdej metryki zasobów możesz ocenić, jak dobrze obciążenie pasuje do wybranego rozmiaru obliczeniowego. Zazwyczaj średnie wartości z sys.resource_stats dają dobrą linię bazową do użycia względem rozmiaru docelowego. Powinien to być podstawowy kij pomiaru.

    • W przypadku baz danych modelu zakupów jednostek DTU:

      Na przykład możesz użyć warstwy usługi Standardowa z rozmiarem obliczeniowym S2. Średnie wartości procentowe użycia dla operacji odczytu i operacji we/wy procesora CPU i operacji we/wy są poniżej 40 procent, średnia liczba procesów roboczych jest niższa niż 50, a średnia liczba sesji jest niższa niż 200. Obciążenie może mieścić się w rozmiarze obliczeniowym S1. Łatwo sprawdzić, czy baza danych pasuje do limitów procesów roboczych i sesji. Aby sprawdzić, czy baza danych mieści się w niższym rozmiarze obliczeniowym, podziel liczbę jednostek DTU mniejszego rozmiaru obliczeniowego na liczbę jednostek DTU bieżącego rozmiaru obliczeniowego, a następnie pomnoż wynik o 100:

      S1 DTU / S2 DTU * 100 = 20 / 50 * 100 = 40

      Wynikiem jest względna różnica wydajności między dwoma rozmiarami obliczeniowymi w procentach. Jeśli użycie zasobu nie przekracza tej ilości, obciążenie może mieścić się w niższym rozmiarze obliczeniowym. Należy jednak przyjrzeć się wszystkim zakresom wartości użycia zasobów i określić, według wartości procentowych, jak często obciążenie bazy danych mieści się w niższym rozmiarze obliczeniowym. Następujące zapytanie zwraca wartość procentową dopasowania na wymiar zasobu na podstawie progu wynoszącego 40 procent obliczonego w tym przykładzie:

       SELECT database_name,
           100*((COUNT(database_name) - SUM(CASE WHEN avg_cpu_percent >= 40 THEN 1 ELSE 0 END) * 1.0) / COUNT(database_name)) AS 'CPU Fit Percent',
           100*((COUNT(database_name) - SUM(CASE WHEN avg_log_write_percent >= 40 THEN 1 ELSE 0 END) * 1.0) / COUNT(database_name)) AS 'Log Write Fit Percent',
           100*((COUNT(database_name) - SUM(CASE WHEN avg_data_io_percent >= 40 THEN 1 ELSE 0 END) * 1.0) / COUNT(database_name)) AS 'Physical Data IO Fit Percent'
       FROM sys.resource_stats
       WHERE start_time > DATEADD(day, -7, GETDATE())
       AND database_name = 'sample' --remove to see all databases
       GROUP BY database_name;
      

      Na podstawie warstwy usługi bazy danych możesz zdecydować, czy obciążenie mieści się w niższym rozmiarze obliczeniowym. Jeśli cel obciążenia bazy danych wynosi 99,9 procent, a poprzednie zapytanie zwraca wartości większe niż 99,9 procent dla wszystkich trzech wymiarów zasobów, obciążenie prawdopodobnie mieści się w niższym rozmiarze obliczeniowym.

      Zapoznanie się z wartością procentową dopasowania daje również wgląd w to, czy należy przejść do następnego wyższego rozmiaru obliczeniowego w celu osiągnięcia celu. Na przykład użycie procesora CPU dla przykładowej bazy danych w ciągu ostatniego tygodnia:

      Średni procent procesora CPU Maksymalny procent procesora CPU
      24.5 100.00

      Średni procesor CPU wynosi około jednej czwartej limitu rozmiaru obliczeniowego, co dobrze pasuje do rozmiaru obliczeniowego bazy danych.

    • W przypadku modelu zakupów jednostek DTU i baz danych modelu zakupów rdzeni wirtualnych:

      Maksymalna wartość pokazuje, że baza danych osiąga limit rozmiaru obliczeniowego. Czy musisz przejść do następnego wyższego rozmiaru obliczeniowego? Sprawdź, ile razy obciążenie osiągnie 100 procent, a następnie porównaj je z celem obciążenia bazy danych.

       SELECT database_name,
           100*((COUNT(database_name) - SUM(CASE WHEN avg_cpu_percent >= 100 THEN 1 ELSE 0 END) * 1.0) / COUNT(database_name)) AS 'CPU Fit Percent',
           100*((COUNT(database_name) - SUM(CASE WHEN avg_log_write_percent >= 100 THEN 1 ELSE 0 END) * 1.0) / COUNT(database_name)) AS 'Log Write Fit Percent',
           100*((COUNT(database_name) - SUM(CASE WHEN avg_data_io_percent >= 100 THEN 1 ELSE 0 END) * 1.0) / COUNT(database_name)) AS 'Physical Data IO Fit Percent'
       FROM sys.resource_stats
       WHERE start_time > DATEADD(day, -7, GETDATE())
       AND database_name = 'sample'  --remove to see all databases
       GROUP BY database_name;
      

      Te wartości procentowe to liczba próbek, które można dopasować do obciążenia w bieżącym rozmiarze obliczeniowym. Jeśli to zapytanie zwróci wartość mniejszą niż 99,9 procent dla dowolnego z trzech wymiarów zasobów, średnie obciążenie próbki przekroczyło limity. Rozważ przejście do następnego wyższego rozmiaru obliczeniowego lub użycie technik dostrajania aplikacji w celu zmniejszenia obciążenia bazy danych.

    Uwaga

    W przypadku pul elastycznych można monitorować pojedyncze bazy danych w puli za pomocą metod opisanych w tej sekcji. Można również monitorować pulę jako całość. Informacje na ten temat znajdziesz w artykule Monitor and manage an elastic pool (Monitorowanie puli elastycznej i zarządzanie nią).

Maksymalna liczba współbieżnych żądań

Aby wyświetlić bieżącą liczbę równoczesnych żądań, uruchom to zapytanie w bazie danych użytkownika:

SELECT COUNT(*) AS [Concurrent_Requests]
FROM sys.dm_exec_requests AS R;

Aby przeanalizować obciążenie bazy danych, zmodyfikuj to zapytanie, aby filtrować określoną bazę danych, którą chcesz przeanalizować. Najpierw zaktualizuj nazwę bazy danych z MyDatabase do żądanej bazy danych, a następnie uruchom następujące zapytanie, aby znaleźć liczbę współbieżnych żądań w tej bazie danych:

SELECT COUNT(*) AS [Concurrent_Requests]
FROM sys.dm_exec_requests AS R
    INNER JOIN sys.databases AS D 
        ON D.database_id = R.database_id
AND D.name = 'MyDatabase';

Jest to tylko migawka w jednym punkcie w czasie. Aby lepiej zrozumieć wymagania dotyczące obciążenia i żądań współbieżnych, należy zebrać wiele przykładów w czasie.

Maksymalna liczba współbieżnych zdarzeń logowania

Możesz analizować wzorce użytkowników i aplikacji, aby zrozumieć częstotliwość zdarzeń logowania. Możesz również uruchamiać rzeczywiste obciążenia w środowisku testowym, aby upewnić się, że nie osiągasz tego lub innych limitów omówimy w tym artykule. Nie ma pojedynczego zapytania ani dynamicznego widoku zarządzania (DMV), który może pokazywać współbieżne liczby logowań lub historię.

Jeśli wielu klientów używa tego samego parametry połączenia, usługa uwierzytelnia każde logowanie. Jeśli 10 użytkowników łączy się jednocześnie z bazą danych przy użyciu tej samej nazwy użytkownika i hasła, będzie istnieć 10 jednoczesnych logowań. Ten limit dotyczy tylko czasu logowania i uwierzytelniania. Jeśli ten sam 10 użytkowników łączy się z bazą danych sekwencyjnie, liczba równoczesnych logowań nigdy nie będzie większa niż 1.

Uwaga

Obecnie ten limit nie dotyczy baz danych w pulach elastycznych.

Maksymalna liczba sesji

Aby wyświetlić liczbę bieżących aktywnych sesji, uruchom to zapytanie w bazie danych:

SELECT COUNT(*) AS [Sessions]
FROM sys.dm_exec_connections;

Jeśli analizujesz obciążenie programu SQL Server, zmodyfikuj zapytanie, aby skoncentrować się na określonej bazie danych. To zapytanie pomaga określić możliwe potrzeby sesji dla bazy danych, jeśli rozważasz przeniesienie jej na platformę Azure. Najpierw zaktualizuj nazwę bazy danych z MyDatabase do żądanej bazy danych, a następnie uruchom następujące zapytanie:

SELECT COUNT(*) AS [Sessions]
FROM sys.dm_exec_connections AS C
    INNER JOIN sys.dm_exec_sessions AS S 
        ON (S.session_id = C.session_id)
    INNER JOIN sys.databases AS D 
        ON (D.database_id = S.database_id)
WHERE D.name = 'MyDatabase';

Ponownie te zapytania zwracają liczbę punktów w czasie. Jeśli z czasem zbierzesz wiele przykładów, będziesz mieć najlepszą wiedzę na temat użycia sesji.

Statystyki historyczne sesji można uzyskać, wysyłając zapytanie do widoku wykazu sys.resource_stats i przeglądając kolumnę active_session_count .

Obliczanie rozmiarów baz danych i obiektów

Następujące zapytanie zwraca rozmiar bazy danych (w megabajtach):

-- Calculates the size of the database.
SELECT SUM(CAST(FILEPROPERTY(name, 'SpaceUsed') AS bigint) * 8192.) / 1024 / 1024 AS size_mb
FROM sys.database_files
WHERE type_desc = 'ROWS';

Następujące zapytanie zwraca rozmiar pojedynczych obiektów (w megabajtach) w bazie danych:

-- Calculates the size of individual database objects.
SELECT o.name, SUM(ps.reserved_page_count) * 8.0 / 1024 AS size_mb
FROM sys.dm_db_partition_stats AS ps 
    INNER JOIN sys.objects AS o 
        ON ps.object_id = o.object_id
GROUP BY o.name
ORDER BY size_mb DESC;

Monitorowanie wydajności zapytań

Wolne lub długotrwałe zapytania mogą zużywać znaczne zasoby systemowe. W tej sekcji pokazano, jak używać dynamicznych widoków zarządzania do wykrywania kilku typowych problemów z wydajnością zapytań przy użyciu dynamicznego widoku zarządzania sys.dm_exec_query_stats . Widok zawiera jeden wiersz na instrukcję zapytania w ramach buforowanego planu, a okres istnienia wierszy jest powiązany z samym planem. Gdy plan zostanie usunięty z pamięci podręcznej, odpowiednie wiersze zostaną wyeliminowane z tego widoku.

Znajdowanie najważniejszych zapytań według czasu procesora CPU

Poniższy przykład zwraca informacje o 15 pierwszych zapytaniach sklasyfikowanych według średniego czasu procesora CPU na wykonanie. W tym przykładzie zapytania są agregowane zgodnie z ich skrótem zapytania, dzięki czemu zapytania równoważne logicznie są grupowane według skumulowanego użycia zasobów.

SELECT TOP 15 query_stats.query_hash AS "Query Hash",
    SUM(query_stats.total_worker_time) / SUM(query_stats.execution_count) AS "Avg CPU Time",
     MIN(query_stats.statement_text) AS "Statement Text"
FROM
    (SELECT QS.*,
        SUBSTRING(ST.text, (QS.statement_start_offset/2) + 1,
            ((CASE statement_end_offset
                WHEN -1 THEN DATALENGTH(ST.text)
                ELSE QS.statement_end_offset END
            - QS.statement_start_offset)/2) + 1) AS statement_text
FROM sys.dm_exec_query_stats AS QS
    CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) AS ST
    ) AS query_stats
GROUP BY query_stats.query_hash
ORDER BY 2 DESC;
Monitorowanie planów zapytań dla skumulowanego czasu procesora CPU

Nieefektywny plan zapytania może również zwiększyć użycie procesora CPU. Poniższy przykład określa, które zapytanie używa najbardziej skumulowanego procesora CPU w najnowszej historii.

SELECT
    highest_cpu_queries.plan_handle,
    highest_cpu_queries.total_worker_time,
    q.dbid,
    q.objectid,
    q.number,
    q.encrypted,
    q.[text]
FROM
    (SELECT TOP 15
            qs.plan_handle,
            qs.total_worker_time
        FROM
            sys.dm_exec_query_stats AS qs
        ORDER BY qs.total_worker_time desc
    ) AS highest_cpu_queries
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS q
ORDER BY highest_cpu_queries.total_worker_time DESC;

Monitorowanie zablokowanych zapytań

Powolne lub długotrwałe zapytania mogą przyczynić się do nadmiernego użycia zasobów i być konsekwencją zablokowanych zapytań. Przyczyną blokowania może być słaba konstrukcja aplikacji, złe plany zapytań, brak przydatnych indeksów itd.

Możesz użyć sys.dm_tran_locks widoku, aby uzyskać informacje o bieżącym działaniu blokowania w bazie danych. Na przykład kod zobacz sys.dm_tran_locks. Aby uzyskać więcej informacji na temat rozwiązywania problemów z blokowaniem, zobacz Omówienie i rozwiązywanie problemów z blokowaniem usługi Azure SQL.

Monitorowanie zakleszczeń

W niektórych przypadkach co najmniej dwa zapytania mogą wzajemnie się blokować, co powoduje zakleszczenie.

Możesz utworzyć dane śledzenia zdarzeń rozszerzonych bazy danych w usłudze Azure SQL Database w celu przechwytywania zdarzeń zakleszczenia, a następnie znajdować powiązane zapytania i ich plany wykonywania w magazynie zapytań. Dowiedz się więcej w artykule Analizowanie i zapobieganie zakleszczeniom w usłudze Azure SQL Database, w tym laboratorium powodujące zakleszczenie w aplikacji AdventureWorksLT. Dowiedz się więcej o typach zasobów, które mogą zakleszczeć.