Überwachen der Microsoft Azure SQL-Datenbankleistung mit dynamischen Verwaltungssichten

Gilt für:Azure SQL-Datenbank

Die Microsoft Azure SQL-Datenbank unterstützt eine Teilmenge dynamischer Verwaltungssichten für die Diagnose von Leistungsproblemen, die auf blockierte Abfragen oder Abfragen mit langen Laufzeiten, fehlerhafte Abfragepläne und mehr zurückzuführen sind.

Dieser Artikel enthält Informationen zum Erkennen häufiger Leistungsprobleme mithilfe von dynamischen Verwaltungssichten über T-SQL. Sie können ein beliebiges Abfragetool verwenden, z. B.:

Berechtigungen

In Azure SQL Datenbank erfordert die Abfrage einer DMV je nach Berechnungsgröße und Bereitstellungsoption entweder die VIEW DATABASE STATE-Berechtigung oder die VIEW SERVER STATE-Berechtigung. Letztere kann über die Mitgliedschaft in der ##MS_ServerStateReader##-Serverrolle erteilt werden.

Zum Erteilen der VIEW DATABASE STATE-Berechtigung für einen bestimmten Datenbankbenutzer führen Sie z. B. die folgende Abfrage aus:

GRANT VIEW DATABASE STATE TO database_user;

Um der ##MS_ServerStateReader##-Serverrolle die Mitgliedschaft für eine Anmeldung zum logischen Server in Azure zu gewähren, stellen Sie eine Verbindung zur master-Datenbank her und führen Sie anschließend z. B. die folgende Abfrage aus:

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

Bei einer lokalen Instanz von SQL Server und in Azure SQL Managed Instance geben dynamische Verwaltungssichten Informationen zum Serverzustand zurück. In Azure SQL-Datenbank werden lediglich Informationen zur aktuellen logischen Datenbank zurückgegeben.

Identifizieren von CPU-Leistungsproblemen

Wenn der CPU-Verbrauch über einen längeren Zeitraum über 80 % liegt, berücksichtigen Sie die folgenden Schritte zur Problembehandlung, unabhängig davon, ob das CPU-Problem jetzt auftritt oder in der Vergangenheit aufgetreten ist.

Das CPU-Problem tritt jetzt auf

Wenn das Problem aktuell auftritt, sind zwei Szenarien möglich:

Viele einzelne Abfragen, die zusammen zu einer hohen CPU-Auslastung führen
  • Verwenden Sie die folgende Abfrage, um die häufigsten Abfragehashes zu identifizieren:

    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;
    
Abfragen mit langer Ausführungszeit, die CPU-Ressourcen verbrauchen, werden noch ausgeführt
  • Verwenden Sie die folgende Abfrage, um diese Abfragen zu identifizieren:

    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
    

Das CPU-Problem ist in der Vergangenheit aufgetreten

Wenn das Problem in der Vergangenheit aufgetreten ist und Sie eine Ursachenanalyse durchführen möchten, verwenden Sie den Abfragespeicher. Benutzer mit Datenbankzugriff können T-SQL verwenden, um die Daten aus dem Abfragespeicher abzufragen. Die Standardkonfigurationen des Abfragespeichers verwenden eine Granularität von 1 Stunde.

  1. Verwenden Sie die folgende Abfrage, um die Aktivität für Abfragen mit hohem CPU-Verbrauch zu untersuchen. Diese Abfrage gibt die häufigsten 15 Abfragen mit CPU-Ressourcenverbrauch zurück. Denken Sie daran, rsi.start_time >= DATEADD(hour, -2, GETUTCDATE() zu ändern:

    -- 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. Sobald Sie die problematischen Abfragen identifiziert haben, sollten Sie diese optimieren, um die CPU-Auslastung zu reduzieren. Wenn Sie keine Zeit haben, die Abfragen zu optimieren, können Sie auch das Servicelevelziel der Datenbank erweitern, um das Problem zu umgehen.

Weitere Informationen zur Behandlung von Problemen mit der CPU-Leistung in Azure SQL-Datenbank finden Sie unter Diagnostizieren und Behandeln von Problemen mit hoher CPU-Auslastung in Azure SQL-Datenbank.

Identifizieren von Problemen mit der E/A-Leistung

Beim Identifizieren von Problemen mit der E/A-Leistung des Speichers sind die wichtigsten Wartetypen, die mit E/A-Problemen verbunden sind:

  • PAGEIOLATCH_*

    Für E/A-Probleme bei Datendateien (einschließlich PAGEIOLATCH_SH, PAGEIOLATCH_EX, PAGEIOLATCH_UP). Wenn der Name des Wartetyps die Buchstaben EA enthält, verweist er auf ein E/A-Problem. Wenn der Name des Pagelatch-Wartevorgangs die Buchstaben EA nicht enthält, verweist er auf einen anderen Problemtyp (z. B. einen tempdb-Konflikt).

  • WRITE_LOG

    Gilt für E/A-Probleme bei Transaktionsprotokollen.

Das E/A-Problem tritt jetzt auf

Verwenden Sie sys.dm_exec_requests oder sys.dm_os_waiting_tasks, um wait_type und wait_time anzuzeigen.

Identifizieren der Daten- und Protokoll-E/A-Nutzung

Verwenden Sie die folgende Abfrage, um die Daten- und Protokoll-E/A-Nutzung zu identifizieren. Wenn die Daten- oder Protokoll-E/A über 80 % liegt, bedeutet dies, dass die verfügbaren E/A-Ressourcen für die Azure SQL-Datenbank-Dienstebene belegt sind.

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;

Weitere Beispiele für die Verwendung von sys.dm_db_resource_stats finden Sie weiter unten in diesem Artikel im Abschnitt Überwachen der Ressourcennutzung .

Wenn das E/A-Limit erreicht ist, haben Sie zwei Möglichkeiten:

  • Upgrade der Computegröße oder Dienstebene
  • Identifikation und Optimierung der Abfragen, die die meisten E/A-Ressourcen verbrauchen

Für Option 2 können Sie die folgende Abfrage für pufferbezogene E/A-Vorgänge im Abfragespeicher verwenden, um die letzten zwei Stunden der überwachten Aktivitäten anzuzeigen:

-- 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
Anzeigen sämtlicher E/A-Vorgänge für WRITELOG-Wartevorgänge

Wenn der Wartetyp WRITELOG ist, verwenden Sie die folgende Abfrage, um sämtliche Protokoll-E/A-Vorgänge nach Anweisung anzuzeigen:

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

Identifizieren von tempdb-Leistungsproblemen

Wenn Sie Probleme mit der E/A-Leistung feststellen, ist PAGELATCH_* (nicht PAGEIOLATCH_*) der häufigste Wartetyp in Zusammenhang mit tempdb-Problemen. PAGELATCH_*-Wartevorgänge bedeuten jedoch nicht immer, dass ein tempdb-Konflikt vorliegt. Dieser Wartevorgang kann auch auf einen Konflikt mit Benutzerobjekt-Datenseiten aufgrund von gleichzeitigen Anforderungen an dieselbe Datenseite hinweisen. Zur genaueren Ermittlung, ob es sich um einen tempdb-Konflikt handelt, verwenden Sie sys.dm_exec_requests, um zu bestätigen, dass der Wert von wait_resource mit 2:x:y beginnt, wobei „2“ die tempdb-Datenbank-ID, x die Datei-ID und y die Seiten-ID ist.

Bei tempdb-Konflikten besteht eine gängige Methode darin, den Anwendungscode, der tempdb benötigt, zu reduzieren oder neu zu schreiben. In folgenden Bereichen wird tempdb meist genutzt:

  • Temporäre Tabellen
  • Tabellenvariablen
  • Tabellenwertparameter
  • Versionsspeichernutzung (in Zusammenhang mit Transaktionen mit langer Ausführungszeit)
  • Abfragen mit Abfrageplänen, die Sortiervorgänge, Hashjoins und Spoolvorgänge verwenden

Weitere Informationen finden Sie unter tempdb in Azure SQL.

Häufige Abfragen, die Tabellenvariablen und temporäre Tabellen verwenden

Verwenden Sie die folgende Abfrage, um die häufigsten Abfragen zu identifizieren, die Tabellenvariablen und temporäre Tabellen verwenden:

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

Identifizieren von Transaktionen mit langer Ausführungszeit

Verwenden Sie die folgende Abfrage, um Transaktionen mit langer Ausführungszeit zu identifizieren. Transaktionen mit langer Ausführungszeit verhindern das Bereinigen von persistenten Versionsspeichern (PVS). Weitere Informationen finden Sie unter Problembehandlung der beschleunigten Datenbankwiederherstellung.

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;

Identifizieren von Leistungsproblemen bei Wartevorgängen für Speicherzuweisung

Wenn der häufigste Wartetyp RESOURCE_SEMAPHORE ist und kein Problem mit einer hohen CPU-Auslastung vorliegt, besteht möglicherweise ein Problem bei Wartevorgängen für die Speicherzuweisung.

Ermitteln Sie, ob RESOURCE_SEMAPHORE ein häufiger Wartetyp ist.

Verwenden Sie die folgende Abfrage, um zu ermitteln, ob ein RESOURCE_SEMAPHORE-Wartevorgang ein häufiger Wartevorgang ist. Indikativ wäre auch ein steigender Wartezeitrang von RESOURCE_SEMAPHORE im aktuellen Verlauf. Weitere Informationen zur Behandlung von Problemen mit wartenden Speicherzuweisungen finden Sie unter Behandeln von Problemen mit geringer Leistung oder geringem Arbeitsspeicher aufgrund von Speicherzuweisungen in 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;
Ermitteln von Anweisungen mit hohem Arbeitsspeicherverbrauch

Wenn in Azure SQL-Datenbank Fehler auftreten, weil nicht genügend Arbeitsspeicher vorhanden ist, finden Sie hilfreiche Informationen unter sys.dm_os_out_of_memory_events. Weitere Informationen finden Sie unter Behandeln von Fehlern mit unzureichendem Arbeitsspeicher mit Azure SQL-Datenbanken.

Ändern Sie zunächst das folgende Skript, um die relevanten Werte von start_time und end_time zu aktualisieren. Verwenden Sie anschließend die folgende Abfrage, um Anweisungen mit hohem Arbeitsspeicherverbrauch zu identifizieren:

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;
Identifizieren der 10 häufigsten aktiven Speicherzuweisungen

Verwenden Sie die folgende Abfrage, um die 10 häufigsten aktiven Speicherzuweisungen zu identifizieren:

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;

Überwachen von Verbindungen

Sie können die Ansicht sys.dm_exec_connections zum Abrufen von Informationen über die Verbindungen verwenden, die mit einer bestimmten Datenbank oder einem Pool für elastische Datenbanken hergestellt wurden, sowie von Details zu den einzelnen Verbindungen. Darüber hinaus ist die Sicht sys.dm_exec_sessions für das Abrufen von Informationen zu allen aktiven Benutzerverbindungen und internen Aufgaben nützlich.

Anzeigen aktueller Sitzungen

Verwenden Sie die folgende Abfrage zum Abrufen der Informationen zur aktuellen Verbindung. Um alle Sitzungen anzuzeigen, entfernen Sie die WHERE-Klausel.

Sie sehen nur dann alle ausgeführten Sitzungen in der Datenbank, wenn Sie beim Ausführen der Sichten sys.dm_exec_requests und sys.dm_exec_sessions über die VIEW DATABASE STATE-Berechtigung für die Datenbank verfügen. Andernfalls sehen Sie nur die aktuelle Sitzung.

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

Überwachen der Ressourcennutzung

Sie können die Azure SQL Datenbankressourcennutzung auf Abfrageebene überwachen, indem Sie Query Performance Insight für Azur SQL-Datenbank im Azure-Portal oder den Abfragespeicher verwenden.

Außerdem lässt sich die Nutzung über diese Sichten überwachen:

sys.dm_db_resource_stats

Sie können die Sicht sys.dm_db_resource_stats in jeder Datenbank verwenden. Die Sicht sys.dm_db_resource_stats enthält Daten zur Ressourcennutzung in der letzten Zeit relativ zur Dienstebene. Durchschnittliche Prozentsätze für CPU, Dateneingang/-ausgang, Protokollschreibvorgänge und Arbeitsspeicher werden alle 15 Sekunden aufgezeichnet und eine Stunde lang aufbewahrt.

Da diese Ansicht eine detailliertere Darstellung der Ressourcennutzung ist, sollten Sie für alle Analysen des aktuellen Zustands oder für die Problembehandlung zuerst sys.dm_db_resource_stats verwenden. Mit dieser Abfrage wird beispielsweise die durchschnittliche und maximale Ressourcennutzung für die aktuelle Datenbank in der letzten Stunde angezeigt:

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;  

Beispiele für andere Abfragen finden Sie unter sys.dm_db_resource_stats.

sys.resource_stats

Die Sicht sys.resource_stats in der master-Datenbank umfasst zusätzliche Informationen, die beim Überwachen der Leistung Ihrer Datenbank innerhalb der jeweiligen Dienstebene und Computegröße nützlich sind. Die Daten werden alle fünf Minuten gesammelt und c.a. 14 Tage lang aufbewahrt. Diese Sicht ist für eine längere Verlaufsanalyse der Ressourcennutzung Ihrer Datenbank hilfreich.

Der folgende Graph zeigt die CPU-Ressourcennutzung für eine Premium-Datenbank mit der Computegröße P2 für jede Stunde einer Woche. Dieser Graph beginnt mit einem Montag und zeigt fünf Arbeitstage und dann das Wochenende, an dem die Anwendung deutlich weniger gefragt ist.

Screenshot: Beispieldiagramm der Nutzung der Datenbankressource.

Die Daten verdeutlichen, dass diese Datenbank derzeit über eine CPU-Spitzenlast von etwas mehr als 50 Prozent CPU-Auslastung in Bezug zur Computegröße P2 verfügt (Dienstagmittag). Falls die CPU der entscheidende Faktor im Ressourcenprofil der Anwendung ist, entscheiden Sie sich ggf. für die Computegröße P2, um die Bewältigung der Workload stets sicherstellen zu können. Wenn eine Anwendung im Laufe der Zeit voraussichtlich größer wird, ist die Verwendung eines zusätzlichen Ressourcenpuffers ratsam, damit für die Anwendung nie der Grenzwert für die Leistungsebene erreicht wird. Wenn Sie die Computegröße erhöhen, können Sie für den Kunden sichtbare Fehler vermeiden. Diese können ggf. auftreten, wenn eine Datenbank nicht über genügend Leistung zum effektiven Verarbeiten von Anforderungen verfügt, vor allem in latenzsensiblen Umgebungen. Ein Beispiel ist eine Datenbank für eine Anwendung, mit der Webseiten basierend auf den Ergebnissen von Datenbankaufrufen farbig gestaltet werden.

Andere Anwendungstypen können denselben Graphen unter Umständen anders interpretieren. Wenn eine Anwendung beispielsweise jeden Tag versucht, Gehaltsabrechnungsdaten zu verarbeiten, und dasselbe Diagramm gilt, wird diese Art von „Batchauftrag“-Modell bei der Computegröße P1 wahrscheinlich zufriedenstellend ausgeführt. Computegröße P1 verfügt über 100 DTUs im Vergleich zu 200 DTUs bei Computegröße P2. Computegröße P1 stellt gegenüber Computegröße P2 die halbe Leistungsfähigkeit bereit. Eine Nutzung von 50 Prozent CPU-Auslastung bei P2 entspricht also 100 Prozent CPU-Auslastung bei P1. Wenn die Anwendung nicht über Timeouts verfügt, spielt es unter Umständen keine Rolle, ob ein Auftrag 2 oder 2,5 Stunden dauert, solange er noch am selben Tag abgeschlossen wird. Für eine Anwendung in dieser Kategorie reicht wahrscheinlich die Computegröße P1 aus. Sie können die Tatsache nutzen, dass es am Tag Zeiten gibt, in denen die Ressourcennutzung niedriger ist. Dies bedeutet, dass „Spitzen“ ggf. in einen der Zeiträume später am Tag verlagert werden können. Die Computegröße P1 ist für diese Art von Anwendung ggf. gut geeignet (und spart Kosten), solange die Aufträge jeden Tag pünktlich abgeschlossen werden können.

Die Datenbank-Engine macht die Informationen zum Ressourcenverbrauch für jede aktive Datenbank in der Sicht sys.resource_stats der master-Datenbank jedes Servers verfügbar. Die Daten in der Tabelle werden zu Intervallen von fünf Minuten zusammengefasst. Bei den Dienstebenen „Basic“, „Standard“ und „Premium“ kann es länger als fünf Minuten dauern, bis sie in der Tabelle angezeigt werden. Diese Daten sind also besser für Verlaufsanalysen als für Analysen nahezu in Echtzeit geeignet. Fragen Sie die Sicht sys.resource_stats ab, um den kürzlichen Verlauf einer Datenbank anzuzeigen und zu überprüfen, ob die gewählte Reservierung zur gewünschten Leistung zur richtigen Zeit geführt hat.

Hinweis

In Azure SQL-Datenbank ist eine Verbindung mit der master-Datenbank erforderlich, um sys.resource_stats in den folgenden Beispielen abzufragen.

Dieses Beispiel veranschaulicht, wie die Daten in dieser Sicht verfügbar gemacht werden:

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

Das nächste Beispiel zeigt verschiedene Möglichkeiten zur Verwendung der sys.resource_stats-Katalogsicht, um Informationen zur Ressourcennutzung durch Ihre Datenbank abzurufen:

  1. Um die Ressourcennutzung der vergangenen Woche für die Benutzerdatenbank userdb1zu untersuchen, können Sie diese Abfrage ausführen, indem Sie Ihren eigenen Datenbanknamen ersetzen:

    SELECT *
    FROM sys.resource_stats
    WHERE database_name = 'userdb1' 
        AND start_time > DATEADD(day, -7, GETDATE())
    ORDER BY start_time DESC;
    
  2. Um auszuwerten, wie gut Ihre Workload zur Computegröße passt, müssen Sie die einzelnen Aspekte der Ressourcenmetriken untersuchen: CPU, Lesevorgänge, Schreibvorgänge, Anzahl von Workern und Anzahl von Sitzungen. Hier ist eine überarbeitete Abfrage mit sys.resource_stats, um die Durchschnitts- und Höchstwerte dieser Ressourcenmetriken für jede Dienstebene zu melden, für die die Datenbank bereitgestellt wurde:

    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. Mit diesen Informationen zu den Durchschnitts- und Höchstwerten der einzelnen Ressourcenmetriken können Sie bewerten, wie gut Ihre Workload zur ausgewählten Computegröße passt. Normalerweise erhalten Sie mit den Durchschnittswerten aus sys.resource_stats eine gute Grundlage gegenüber der Zielgröße. Dies sollte Ihre primäre Messlatte sein.

    • Für Datenbanken vom Typ DTU-Kaufmodell:

      Beispielsweise können Sie die Standard-Dienstebene mit der Computegröße S2 verwenden. Die durchschnittlichen Nutzungsprozentsätze für CPU- und I/O-Lese- und -Schreibvorgänge liegen unter 40 Prozent, die durchschnittliche Anzahl von Workern unter 50 und die durchschnittliche Anzahl von Sitzungen unter 200. Für diese Workload ist unter Umständen die Computegröße S1 geeignet. Es ist leicht zu erkennen, ob Ihre Datenbank die Grenzen für Worker und Sitzungen einhält. Um zu ermitteln, ob sich für eine Datenbank eine niedrigere Computegröße eignet, dividieren Sie die DTU-Anzahl der niedrigeren Computegröße durch die DTU-Anzahl der aktuellen Computegröße, und multiplizieren Sie dann das Ergebnis mit 100:

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

      Als Ergebnis erhalten Sie den relativen Leistungsunterschied zwischen den beiden Computegrößen in Prozent. Wenn die Ressourcennutzung diesen Betrag nicht überschreitet, kann für Ihre Workload ggf. die niedrigere Computegröße geeignet sein. Sie sollten sich aber alle Bereiche der Ressourcennutzungswerte ansehen und anhand des Prozentsatzes ermitteln, wie oft für Ihre Datenbankworkload die niedrigere Computegröße geeignet wäre. Mit der folgenden Abfrage wird der Prozentsatz für die Eignung pro Ressourcendimension basierend auf dem in diesem Beispiel berechneten Schwellenwert von 40% ausgegeben:

       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;
      

      Auf Basis Ihrer Dienstebene für die Datenbank können Sie entscheiden, ob für Ihre Workload die niedrigere Computegröße geeignet ist. Wenn das Ziel für die Datenbankworkload 99,9 Prozent beträgt und die obige Abfrage höhere Werte als 99,9 Prozent für alle drei Ressourcendimensionen zurückgibt, ist die Wahrscheinlichkeit hoch, dass sich die niedrigere Computegröße für Ihre Workload eignet.

      Wenn Sie sich den Prozentsatz für die Eignung ansehen, erhalten Sie auch Informationen dazu, ob Sie zur nächsthöheren Computegröße wechseln müssen, um das Ziel zu erreichen. Beispielsweise die CPU-Auslastung bei einer Beispieldatenbank in der vergangenen Woche:

      Durchschnittlicher CPU-Prozentwert Maximaler CPU-Prozentwert
      24,5 100,00

      Der durchschnittliche CPU-Wert beträgt ca. ein Viertel der Obergrenze der Computegröße. Dies würde also gut zur Computegröße der Datenbank passen.

    • Für Datenbanken vom Typ DTU-Kaufmodell und vCore-Kaufmodell :

      Der Höchstwert zeigt, dass die Datenbank die Obergrenze der Computegröße erreicht. Müssen Sie also zur nächsthöheren Computegröße wechseln? Prüfen Sie, wie häufig Ihre Workload 100 Prozent erreicht, und vergleichen Sie dies dann mit Ihrem Ziel für die Datenbankworkload.

       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;
      

      Diese Prozentsätze sind die Anzahl der Stichproben, die Ihre Workload bei der aktuellen Computegröße unterbringt. Wenn diese Abfrage einen Wert von weniger als 99,9 Prozent für eine der drei Ressourcendimensionen zurückgibt, überschreitet Ihre durchschnittliche Stichproben-Workload die Grenzwerte. Ziehen Sie entweder eine Umstellung auf die nächsthöhere Computegröße oder die Nutzung von Verfahren zur Anwendungsoptimierung in Betracht, um die Auslastung der Datenbank zu reduzieren.

    Hinweis

    In Pools für elastische Datenbanken können Sie einzelne Datenbanken mit den in diesem Abschnitt beschriebenen Methoden überwachen. Sie können den Pool auch als Ganzes überwachen. Informationen dazu finden Sie unter Überwachen und Verwalten von Pools für elastische Datenbanken.

Maximale Anzahl von gleichzeitigen Anforderungen

Um die aktuelle Anzahl gleichzeitiger Anforderungen anzuzeigen, führen Sie diese Abfrage an Ihrer Benutzerdatenbank aus:

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

Um den Workload einer Datenbank zu analysieren, ändern Sie diese Abfrage so ab, dass nach der zu analysierenden Datenbank gefiltert wird. Ändern Sie zunächst den Namen der Datenbank von MyDatabase in den gewünschten Datenbanknamen, und führen Sie dann die folgende Abfrage aus, um die Anzahl gleichzeitiger Anforderungen in dieser Datenbank zu ermitteln:

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

Dies ist nur eine Momentaufnahme zu einem bestimmten Zeitpunkt. Um ein besseres Verständnis Ihrer Workload und der Anforderungen an gleichzeitige Anforderungen zu entwickeln, müssten Sie im Laufe der Zeit viele Beispiele sammeln.

Maximale Anzahl von gleichzeitigen Ereignissen

Sie können Ihre Benutzer- und Anmeldungsmuster analysieren, um Informationen zur Häufigkeit der Anmeldungsereignisse zu erhalten. Sie können auch reale Auslastungen in einer Testumgebung ausführen, um sicherzustellen, dass diese oder andere in diesem Artikel beschriebene Grenzwerte nicht erreicht oder überschritten werden. Es gibt keine einzelne Abfrage oder dynamische Verwaltungssicht (DMV), mit der Sie die Anzahl von gleichzeitigen Anmeldungen oder den Verlauf anzeigen können.

Der Dienst authentifiziert jede Anmeldung, wenn mehrere Clients die gleiche Verbindungszeichenfolge verwenden. Wenn zehn Benutzer gleichzeitig mit dem gleichen Benutzernamen und Kennwort mit einer Datenbank verbunden sind, wären dies also zehn gleichzeitige Anmeldungen. Diese Obergrenze gilt nur für die Dauer der Anmeldung und Authentifizierung. Wenn sich die gleichen zehn Benutzer nacheinander an der Datenbank anmelden, wäre die Anzahl von gleichzeitigen Anmeldungen niemals höher als 1.

Hinweis

Diese Beschränkung gilt derzeit nicht für Datenbanken in Pools für elastische Datenbanken.

Maximale Anzahl von Sitzungen

Um die Anzahl aktueller aktiver Sitzungen anzuzeigen, führen Sie diese Abfrage in Ihrer Datenbank aus:

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

Wenn Sie die SQL Server-Workload analysieren, ändern Sie die Abfrage für eine spezifische Datenbank ab. Diese Abfrage ist zum Ermitteln der möglichen Sitzungsanforderungen für die Datenbank hilfreich, wenn Sie eine Umstellung auf Azure erwägen. Ändern Sie zunächst den Namen der Datenbank von MyDatabase in den gewünschten Datenbanknamen, und führen Sie dann die folgende Abfrage aus:

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

Diese Abfragen geben wieder eine Anzahl zu einem bestimmten Zeitpunkt zurück. Wenn Sie im Laufe der Zeit mehrere Beispielwerte sammeln, können Sie sich am besten über Ihre Sitzungsnutzung informieren.

Sie können Verlaufsstatistiken für Sitzungen abrufen, indem Sie die sys.resource_stats-Katalogsicht abfragen und die Spalte active_session_count überprüfen.

Berechnen von Datenbank- und Objektgrößen

Die folgende Abfrage gibt die Größe Ihrer Datenbank (in Megabyte) zurück:

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

Die folgende Abfrage gibt die Größe der einzelnen Objekte in Ihrer Datenbank in Megabyte zurück:

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

Überwachen der Abfrageleistung

Langsame Abfragen oder Abfragen mit langen Ausführungszeiten können beträchtliche Systemressourcen beanspruchen. In diesem Abschnitt wird veranschaulicht, wie mit dynamischen Verwaltungssichten häufig auftretende Leistungsprobleme bei Abfragen mithilfe der dynamischen Verwaltungssicht sys.dm_exec_query_stats ermittelt werden können. Diese Sicht enthält eine Zeile pro Abfrageanweisung innerhalb des zwischengespeicherten Plans, und die Lebensdauer der Zeilen ist an den Plan selbst gebunden. Wenn ein Plan aus dem Cache entfernt wird, werden die entsprechenden Zeilen aus dieser Sicht entfernt.

Ermitteln der häufigsten Abfragen nach CPU-Zeit

Das folgende Beispiel gibt Informationen über die 15 häufigsten Abfragen gemessen an durchschnittlicher CPU-Zeit pro Ausführung zurück. In diesem Beispiel werden die Abfragen entsprechend ihrem Abfragenhash zusammengefasst, sodass logisch äquivalente Abfragen nach ihrem kumulativen Ressourcenverbrauch gruppiert werden.

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;
Überwachen von Abfrageplänen auf kumulative CPU-Zeit

Auch ein ineffizienter Abfrageplan kann die CPU-Auslastung erhöhen. Im folgenden Beispiel wird ermittelt, welche Abfrage die meiste kumulierte CPU-Auslastung im aktuellen Verlauf benötigt.

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;

Überwachen blockierter Abfragen

Langsame Abfragen oder Abfragen mit langer Laufzeit können zu einer übermäßigen Ressourcennutzung beitragen und auf blockierte Abfragen zurückzuführen sein. Ursache für das Blockieren kann ein mangelhafter Anwendungsentwurf, fehlerhafte Abfragepläne oder ein Mangel an nützlichen Indizes usw. sein.

Informationen zur aktuellen Sperraktivität in der Datenbank können mithilfe der sys.dm_tran_locks-Sicht abgerufen werden. Beispielcode finden Sie unter sys.dm_tran_locks. Weitere Informationen zur Problembehandlung beim Blockieren finden Sie unter Verstehen und Beheben von SQL Server-Blockierungsproblemen.

Überwachen von Deadlocks

In einigen Fällen können zwei oder mehr Abfragen sich gegenseitig blockieren, was zu einem Deadlock führt.

Sie können eine Ablaufverfolgung für erweiterte Ereignisse für eine Datenbank in Azure SQL-Datenbank erstellen, um Deadlockereignisse zu erfassen, und dann verwandte Abfragen und ihre Ausführungspläne im Abfragespeicher suchen. Weitere Informationen finden Sie unter Analysieren und Verhindern von Deadlocks in Azure SQL-Datenbank, einschließlich eines Labs zum Verursachen eines Deadlocks in AdventureWorksLT. Erfahren Sie mehr über Typen von Ressourcen, für die ein Deadlock auftreten kann.