Dela via


Övervaka prestanda med dynamiska hanteringsvyer

gäller för:Azure SQL DatabaseSQL-databas i Fabric

Du kan köra frågor mot dynamiska hanteringsvyer (DMV:er) via T-SQL för att övervaka arbetsbelastningens prestanda och diagnostisera prestandaproblem, vilket kan orsakas av blockerade eller långvariga frågor, resursflaskhalsar, suboptimala frågeplaner med mera.

För övervakning av grafiska frågeresurser använder du Query Store-.

Tips

Överväg Automatisk databasjustering för att automatiskt förbättra frågeprestanda.

Övervaka resursanvändning

Du kan övervaka resursanvändningen på databasnivå med hjälp av följande DMV:er.

sys.dm_db_resource_stats

Eftersom den här vyn innehåller detaljerade resursanvändningsdata använder du först sys.dm_db_resource_stats för aktuell tillståndsanalys eller felsökning. Den här frågan visar till exempel den genomsnittliga och högsta resursanvändningen för den aktuella databasen under den senaste timmen:

SELECT
    database_name = DB_NAME(),
    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',
    MAX(max_worker_percent) AS 'Maximum worker use in percent'
FROM sys.dm_db_resource_stats

Vyn sys.dm_db_resource_stats visar senaste resursanvändningsdata i förhållande till gränserna för beräkningsstorleken. Procentandelar av CPU, data-I/O, loggskrivningar, arbetstrådar och minnesanvändning mot gränsen registreras för varje 15-sekundersintervall och underhålls i ungefär en timme.

Andra exempelfrågor finns i exemplen i sys.dm_db_resource_stats.

sys.resource_stats

Vyn sys.resource_stats i master-databasen har ytterligare information som kan hjälpa dig att övervaka databasens prestanda på den specifika tjänstnivån och beräkningsstorleken. Data samlas in var 5:e minut och underhålls i cirka 14 dagar. Den här vyn är användbar för en mer långsiktig historisk analys av hur databasen använder resurser.

Följande diagram visar cpu-resursanvändningen för en Premium-databas med P2-beräkningsstorleken för varje timme i veckan. Det här diagrammet börjar på en måndag, visar fem arbetsdagar och visar sedan en helg, när mycket mindre händer i programmet.

Skärmbild av ett exempeldiagram över databasresursanvändning.

Från data har den här databasen för närvarande en högsta CPU-belastning på lite över 50 procent CPU-användning i förhållande till P2-beräkningsstorleken (mitt på dagen på tisdag). Om CPU är den dominerande faktorn i programmets resursprofil kan du bestämma att P2 är rätt beräkningsstorlek för att garantera att arbetsbelastningen alltid passar. Om du förväntar dig att ett program ska växa med tiden är det en bra idé att ha en extra resursbuffert så att programmet aldrig når gränsen på prestandanivå. Om du ökar beräkningsstorleken kan du undvika kund synliga fel som kan uppstå när en databas inte har tillräckligt med kraft för att bearbeta begäranden effektivt, särskilt i svarstidskänsliga miljöer.

För andra programtyper kan du tolka samma diagram på olika sätt. Om ett program till exempel försöker bearbeta lönedata varje dag och har samma diagram kan den här typen av "batchjobb"-modell klara sig bra med en P1-beräkningsstorlek. P1-beräkningsstorleken har 100 DTU:er jämfört med 200 DTU:er med P2-beräkningsstorleken. P1-beräkningsstorleken ger hälften av P2-beräkningsstorlekens prestanda. 50 procent av cpu-användningen i P2 är alltså lika med 100 procent cpu-användning i P1. Om programmet inte har tidsgränser kanske det inte spelar någon roll om ett jobb tar 2 timmar eller 2,5 timmar att slutföra, om det görs idag. Ett program i den här kategorin kan förmodligen använda en P1-beräkningsstorlek. Du kan dra nytta av det faktum att det finns tidsperioder under dagen när resursanvändningen är lägre, så att alla "stora toppar" kan spilla över i ett av trågen senare på dagen. P1-beräkningsstorleken kan vara bra för den typen av program (och spara pengar), så länge jobben kan slutföras i tid varje dag.

Databasmotorn exponerar förbrukad resursinformation för varje aktiv databas i sys.resource_stats vy över den master databasen på varje logisk server. Data i vyn aggregeras med 5 minuters intervall. Det kan ta flera minuter innan dessa data visas i tabellen, så sys.resource_stats är mer användbart för historisk analys i stället för analys i nära realtid. Fråga sys.resource_stats för att se den senaste historiken för en databas och kontrollera om den beräkningsstorlek du valde levererade den prestanda du vill ha när det behövs.

Not

Du måste vara ansluten till master-databasen för att köra frågor mot sys.resource_stats i följande exempel.

I det här exemplet visas data i sys.resource_stats:

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

I nästa exempel visas olika sätt att använda sys.resource_stats katalogvyn för att få information om hur databasen använder resurser:

  1. Om du vill titta på den senaste veckans resursanvändning för användardatabasen userdb1kan du köra den här frågan och ersätta ditt eget databasnamn:

    SELECT *
    FROM sys.resource_stats
    WHERE database_name = 'userdb1' 
        AND start_time > DATEADD(day, -7, GETDATE())
    ORDER BY start_time DESC;
    
  2. För att utvärdera hur väl arbetsbelastningen passar beräkningsstorleken måste du öka detaljnivån i varje aspekt av resursmåtten: CPU, data-I/O, loggskrivning, antal arbetare och antal sessioner. Här är en reviderad fråga som använder sys.resource_stats för att rapportera de genomsnittliga och maximala värdena för dessa resursmått, för varje beräkningsstorlek som databasen har etablerats för:

    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)
    ,    'Maximum Requests In %'              = MAX(rs.max_worker_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. Med den här informationen om medelvärdet och maxvärdena för varje resursmått kan du utvärdera hur väl din arbetsbelastning passar in i den beräkningsstorlek som du har valt. Vanligtvis ger genomsnittliga värden från sys.resource_stats dig en bra baslinje att använda mot målstorleken.

    • För databaser i DTU-inköpsmodell .

      Du kan till exempel använda standardtjänstnivån med S2-beräkningsstorlek. Den genomsnittliga användningsprocenten för CPU- och I/O-läsningar och skrivningar är under 40 procent, det genomsnittliga antalet arbetare är under 50 och det genomsnittliga antalet sessioner är under 200. Din arbetsbelastning kan passa in i S1-beräkningsstorleken. Det är enkelt att se om databasen passar i arbets- och sessionsgränserna. Om du vill se om en databas får en lägre beräkningsstorlek delar du upp DTU-numret för den lägre beräkningsstorleken med DTU-numret för din aktuella beräkningsstorlek och multiplicerar sedan resultatet med 100:

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

      Resultatet är den relativa prestandaskillnaden mellan de två beräkningsstorlekarna i procent. Om resursanvändningen inte överskrider den här procentandelen kan din arbetsbelastning passa in i den lägre beräkningsstorleken. Du måste dock titta på alla intervall med resursanvändningsvärden och i procent fastställa hur ofta databasarbetsbelastningen får plats i den lägre beräkningsstorleken. Följande fråga visar anpassningsprocenten per resursdimension, baserat på tröskelvärdet på 40 procent som vi beräknade i det här exemplet.

       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;
      

      Baserat på databastjänstnivån kan du bestämma om din arbetsbelastning ska få den lägre beräkningsstorleken. Om målet för databasens arbetsbelastning är 99,9 procent och föregående fråga returnerar värden som är större än 99,9 procent för alla tre resursdimensionerna, passar din arbetsbelastning troligen in i den lägre beräkningsstorleken.

      Om du tittar på fit-procentandelen får du också en inblick i om du ska gå över till nästa högre beräkningsstorlek för att uppfylla ditt mål. Till exempel cpu-användningen för en exempeldatabas under den senaste veckan:

      Genomsnittlig CPU-procent Maximal cpu-procent
      24.5 100.00

      Den genomsnittliga processorn är ungefär en fjärdedel av gränsen för beräkningsstorleken, vilket skulle passa bra in i databasens beräkningsstorlek.

    • För databaser: DTU-inköpsmodell och vCore-inköpsmodell

      Det maximala värdet visar att databasen når gränsen för beräkningsstorleken. Behöver du flytta till nästa högre beräkningsstorlek? Titta på hur många gånger din arbetsbelastning når 100 procent och jämför den sedan med målet för databasens arbetsbelastning.

       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;
      

      Dessa procentandelar är antalet prover som din arbetsbelastning ryms inom under den nuvarande beräkningsstorleken. Om den här frågan returnerar ett värde som är mindre än 99,9 procent för någon av de tre resursdimensionerna överskred den genomsnittliga arbetsbelastningen i urvalet gränserna. Överväg att antingen flytta till nästa högre beräkningsstorlek eller använda programjusteringstekniker för att minska belastningen på databasen.

sys.dm_elastic_pool_resource_stats

gäller endast för:Azure SQL Database

På samma sätt som sys.dm_db_resource_statstillhandahåller sys.dm_elastic_pool_resource_stats senaste och detaljerade resursanvändningsdata för en elastisk Azure SQL Database-pool. Vyn kan frågas i valfri databas i en elastisk pool för att tillhandahålla resursanvändningsdata för en hel pool i stället för någon specifik databas. De procentvärden som rapporteras av denna DMV ligger mot gränserna för den elastiska poolen, som kan vara högre än gränserna för en databas i poolen.

Det här exemplet visar sammanfattade resursanvändningsdata för den aktuella elastiska poolen under de senaste 15 minuterna:

SELECT dso.elastic_pool_name,
       AVG(eprs.avg_cpu_percent) AS avg_cpu_percent,
       MAX(eprs.avg_cpu_percent) AS max_cpu_percent,
       AVG(eprs.avg_data_io_percent) AS avg_data_io_percent,
       MAX(eprs.avg_data_io_percent) AS max_data_io_percent,
       AVG(eprs.avg_log_write_percent) AS avg_log_write_percent,
       MAX(eprs.avg_log_write_percent) AS max_log_write_percent,
       MAX(eprs.max_worker_percent) AS max_worker_percent,
       MAX(eprs.used_storage_percent) AS max_used_storage_percent,
       MAX(eprs.allocated_storage_percent) AS max_allocated_storage_percent
FROM sys.dm_elastic_pool_resource_stats AS eprs
CROSS JOIN sys.database_service_objectives AS dso
WHERE eprs.end_time >= DATEADD(minute, -15, GETUTCDATE())
GROUP BY dso.elastic_pool_name;

Om du upptäcker att all resursanvändning närmar sig 100% under en längre tid kan du behöva granska resursanvändningen för enskilda databaser i samma elastiska pool för att avgöra hur mycket varje databas bidrar till resursanvändningen på poolnivå.

sys.elastic_pool_resource_stats

gäller endast för:Azure SQL Database

På samma sätt som sys.resource_statsger sys.elastic_pool_resource_stats i master-databasen historiska resursanvändningsdata för alla elastiska pooler på den logiska servern. Du kan använda sys.elastic_pool_resource_stats för historisk övervakning under de senaste 14 dagarna, inklusive analys av användningstrender.

Det här exemplet visar sammanfattade resursanvändningsdata under de senaste sju dagarna för alla elastiska pooler på den aktuella logiska servern. Kör frågan i master-databasen.

SELECT elastic_pool_name,
       AVG(avg_cpu_percent) AS avg_cpu_percent,
       MAX(avg_cpu_percent) AS max_cpu_percent,
       AVG(avg_data_io_percent) AS avg_data_io_percent,
       MAX(avg_data_io_percent) AS max_data_io_percent,
       AVG(avg_log_write_percent) AS avg_log_write_percent,
       MAX(avg_log_write_percent) AS max_log_write_percent,
       MAX(max_worker_percent) AS max_worker_percent,
       AVG(avg_storage_percent) AS avg_used_storage_percent,
       MAX(avg_storage_percent) AS max_used_storage_percent,
       AVG(avg_allocated_storage_percent) AS avg_allocated_storage_percent,
       MAX(avg_allocated_storage_percent) AS max_allocated_storage_percent
FROM sys.elastic_pool_resource_stats
WHERE start_time >= DATEADD(day, -7, GETUTCDATE())
GROUP BY elastic_pool_name
ORDER BY elastic_pool_name ASC;

Samtidiga begäranden

Om du vill se det aktuella antalet samtidiga begäranden kör du den här frågan i användardatabasen:

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

Det här är bara en ögonblicksbild vid en enda tidpunkt. För att få en bättre förståelse för dina krav på arbetsbelastningar och samtidiga begäranden måste du samla in många exempel över tid.

Genomsnittlig begärandefrekvens

Det här exemplet visar hur du hittar den genomsnittliga begärandefrekvensen för en databas eller för databaser i en elastisk pool under en tidsperiod. I det här exemplet är tidsperioden inställd på 30 sekunder. Du kan justera den genom att ändra WAITFOR DELAY-instruktionen. Kör den här frågan i användardatabasen. Om databasen finns i en elastisk pool och om du har tillräcklig behörighet inkluderar resultaten andra databaser i den elastiska poolen.

DECLARE @DbRequestSnapshot TABLE (
                                 database_name sysname PRIMARY KEY,
                                 total_request_count bigint NOT NULL,
                                 snapshot_time datetime2 NOT NULL DEFAULT (SYSDATETIME())
                                 );

INSERT INTO @DbRequestSnapshot
(
database_name,
total_request_count
)
SELECT rg.database_name,
       wg.total_request_count
FROM sys.dm_resource_governor_workload_groups AS wg
INNER JOIN sys.dm_user_db_resource_governance AS rg
ON wg.name = CONCAT('UserPrimaryGroup.DBId', rg.database_id);

WAITFOR DELAY '00:00:30';

SELECT rg.database_name,
       (wg.total_request_count - drs.total_request_count) / DATEDIFF(second, drs.snapshot_time, SYSDATETIME()) AS requests_per_second
FROM sys.dm_resource_governor_workload_groups AS wg
INNER JOIN sys.dm_user_db_resource_governance AS rg
ON wg.name = CONCAT('UserPrimaryGroup.DBId', rg.database_id)
INNER JOIN @DbRequestSnapshot AS drs
ON rg.database_name = drs.database_name;

Aktuella sessioner

Om du vill se antalet aktuella aktiva sessioner kör du den här frågan i databasen:

SELECT COUNT(*) AS [Sessions]
FROM sys.dm_exec_sessions
WHERE is_user_process = 1;

Den här frågan returnerar ett antal tidpunkter. Om du samlar in flera exempel över tid har du den bästa förståelsen för din sessionsanvändning.

Senaste historiken för begäranden, sessioner och arbetare

Det här exemplet returnerar den senaste historiska användningen av begäranden, sessioner och arbetstrådar för en databas eller för databaser i en elastisk pool. Varje rad representerar en ögonblicksbild av resursanvändningen vid en tidpunkt för en databas. Kolumnen requests_per_second är den genomsnittliga begärandefrekvensen under tidsintervallet som slutar vid snapshot_time. Om databasen finns i en elastisk pool och om du har tillräcklig behörighet inkluderar resultaten andra databaser i den elastiska poolen.

SELECT rg.database_name,
       wg.snapshot_time,
       wg.active_request_count,
       wg.active_worker_count,
       wg.active_session_count,
       CAST(wg.delta_request_count AS decimal) / duration_ms * 1000 AS requests_per_second
FROM sys.dm_resource_governor_workload_groups_history_ex AS wg
INNER JOIN sys.dm_user_db_resource_governance AS rg
ON wg.name = CONCAT('UserPrimaryGroup.DBId', rg.database_id)
ORDER BY snapshot_time DESC;

Beräkna databas- och objektstorlekar

Följande fråga returnerar datastorleken i databasen (i megabyte):

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

Följande fråga returnerar storleken på enskilda objekt (i megabyte) i databasen:

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

Identifiera problem med CPU-prestanda

Det här avsnittet hjälper dig att identifiera enskilda frågor som är de främsta CPU-konsumenterna.

Om cpu-förbrukningen är över 80% under längre tidsperioder bör du överväga följande felsökningssteg om cpu-problemet inträffar nu eller har inträffat tidigare. Du kan också följa stegen i det här avsnittet för att proaktivt identifiera frågor som förbrukar mest processorkraft och finjustera dem. I vissa fall kan du minska cpu-förbrukningen så att du kan skala ned dina databaser och elastiska pooler och spara kostnader.

Felsökningsstegen är desamma för fristående databaser och databaser i en elastisk pool. Kör alla frågor i användardatabasen.

Cpu-problemet inträffar nu

Om problemet uppstår just nu finns det två möjliga scenarier:

Många enskilda frågor som kumulativt förbrukar hög CPU

Använd följande fråga för att identifiera de vanligaste frågorna efter frågehash:

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;

Tidskrävande frågor som förbrukar CPU körs fortfarande

Använd följande fråga för att identifiera dessa frågor:

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

Cpu-problemet inträffade tidigare

Om problemet har uppstått tidigare och du vill göra en rotorsaksanalys, använder du Query Store-. Användare med databasåtkomst kan använda T-SQL för att köra frågor mot Query Store-data. Som standard samlar Query Store in sammanställd frågestatistik för entimmesintervall.

  1. Använd följande fråga för att titta på aktivitet för frågor med hög CPU-användning. Den här frågan returnerar de 15 mest processorkrävande frågorna. Kom ihåg att ändra rsi.start_time >= DATEADD(hour, -2, GETUTCDATE() för att titta på en annan tidsperiod än de senaste två timmarna:

    -- 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. När du har identifierat de problematiska frågorna är det dags att justera dessa frågor för att minska CPU-användningen. Du kan också välja att öka beräkningsstorleken för databasen eller den elastiska poolen för att lösa problemet.

Mer information om hur du hanterar processorprestandaproblem i Azure SQL Database finns i Diagnostisera och felsöka hög CPU på Azure SQL Database.

Identifiera I/O-prestandaproblem

När du identifierar prestandaproblem med indata/utdata för lagring (I/O) är de vanligaste väntetyperna:

  • PAGEIOLATCH_*

    För I/O-problem med datafiler (inklusive PAGEIOLATCH_SH, PAGEIOLATCH_EX, PAGEIOLATCH_UP). Om namnet på väntetypen har I/O- i det, pekar det på ett I/O-problem. Om det inte finns någon I/O- i väntenamnet för sidspärren pekar det på en annan typ av problem som inte är relaterade till lagringsprestanda (till exempel tempdb konkurrens).

  • WRITE_LOG

    För I/O-problem med transaktionsloggar.

Om I/O-problemet inträffar just nu

Använd sys.dm_exec_requests eller sys.dm_os_waiting_tasks för att se wait_type och wait_time.

Identifiera data och logga I/O-användning

Använd följande fråga för att identifiera data och logga I/O-användning.

SELECT
    database_name = DB_NAME()
,   UTC_time = end_time
,   'Data IO In % of Limit'                   = rs.avg_data_io_percent
,   'Log Write Utilization In % of Limit'     = rs.avg_log_write_percent
FROM sys.dm_db_resource_stats AS rs  --past hour only
ORDER BY  rs.end_time DESC;

Fler exempel som använder sys.dm_db_resource_statsfinns i avsnittet Övervaka resursanvändning senare i den här artikeln.

Om I/O-gränsen har nåtts har du två alternativ:

  • Uppgradera beräkningsstorleken eller tjänstnivån
  • Identifiera och finjustera de frågor som förbrukar mest I/O.

Om du vill identifiera de vanligaste frågorna efter I/O-relaterade väntetider kan du använda följande Query Store-fråga för att visa de senaste två timmarna av spårad aktivitet:

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

Du kan också använda vyn sys.query_store_runtime_stats och fokusera på frågor med stora värden i kolumnerna avg_physical_io_reads och avg_num_physical_io_reads.

Visa total in- och utmatning av loggar för WRITELOG-fördröjningar

Om väntetypen är WRITELOG, använd följande fråga för att visa total logg-I/O per instruktion:

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

Identifiera problem med tempdb-prestanda

Vanliga väntetyper som är associerade med tempdb problem är PAGELATCH_* (inte PAGEIOLATCH_*). Men PAGELATCH_* väntetider betyder inte alltid att du har tempdb konkurrens. Den här väntetiden kan också innebära att du har konkurrens om användarobjektsdatasidor på grund av samtidiga begäranden som riktar sig till samma datasida. Om du vill bekräfta tempdb konkurrens ytterligare använder du sys.dm_exec_requests för att bekräfta att värdet wait_resource börjar med 2:x:y där 2 är tempdb är databas-ID, x är fil-ID och y är sid-ID.

För tempdb konkurrens är en vanlig metod att minska eller skriva om den programkod som förlitar sig på tempdb. Vanliga tempdb användningsområden är:

  • Temporära tabeller
  • Tabellvariabler
  • Tabellvärdesparametrar
  • Frågor som har frågeplaner som använder sortering, hashkopplingar och spooler

Mer information finns i tempdb i Azure SQL.

Alla databaser i en elastisk pool delar samma tempdb databas. En hög tempdb utrymmesanvändning av en databas kan påverka andra databaser i samma elastiska pool.

De vanligaste frågorna som använder tabellvariabler och temporära tabeller

Använd följande fråga för att identifiera de vanligaste frågorna som använder tabellvariabler och temporära tabeller:

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

Identifiera tidskrävande transaktioner

Använd följande fråga för att identifiera långvariga transaktioner. Tidskrävande transaktioner förhindrar rensning av beständiga versionslager (PVS). Mer information finns i Felsöka accelererad databasåterställning.

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;

Identifiera prestandaproblem kopplade till minnestilldelning och väntetider

Om den vanligaste väntetypen är RESOURCE_SEMAPHOREkan det finnas ett problem med att väntar på minnestilldelning där frågor inte kan börja exekvera förrän de har fått ett tillräckligt stort minnestilldelning.

Ta reda på om en RESOURCE_SEMAPHORE väntetid är en toppvänte

Använd följande fråga för att avgöra om en RESOURCE_SEMAPHORE väntetid är en toppväntetid. Också indikerande skulle vara en stigande väntetidsrankning av RESOURCE_SEMAPHORE i senare tid. Mer information om hur du felsöker problem med minnestilldelning finns i Felsöka problem med långsamma prestanda eller minnesbrist som orsakas av minnesbidrag i 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;

Identifiera höga minneskrävande instruktioner

Om du stöter på minnesfel i Azure SQL Database, granska sys.dm_os_out_of_memory_events. Mer information finns i Felsöka minnesfel med Azure SQL Database.

Ändra först följande skript för att uppdatera relevanta värden för start_time och end_time. Kör sedan följande fråga för att identifiera höga minneskrävande instruktioner:

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

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;

Identifiera de 10 främsta aktiva minnestilldelningarna

Använd följande fråga för att identifiera de 10 främsta aktiva minnestilldelningarna:

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;

Övervaka anslutningar

Du kan använda vyn sys.dm_exec_connections för att hämta information om anslutningar som upprättats till en specifik databas och information om varje anslutning. Om en databas finns i en elastisk pool och du har tillräcklig behörighet returnerar vyn uppsättningen anslutningar för alla databaser i den elastiska poolen. Dessutom är vyn sys.dm_exec_sessions användbar när du hämtar information om alla aktiva användaranslutningar och interna uppgifter.

Visa aktuella sessioner

Följande fråga hämtar information för din aktuella anslutning och session. Om du vill visa alla anslutningar och sessioner tar du bort WHERE-satsen.

Du kan se alla pågående körningssessioner i databasen endast om du har VIEW DATABASE STATE-behörighet på databasen när du kör vyerna sys.dm_exec_requests och sys.dm_exec_sessions. Annars ser du bara den aktuella sessionen.

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

Övervaka frågeprestanda

Långsamma eller tidskrävande frågor kan förbruka betydande systemresurser. Det här avsnittet visar hur du använder dynamiska hanteringsvyer för att identifiera några vanliga problem med frågeprestanda med hjälp av vyn sys.dm_exec_query_stats dynamisk hantering. Vyn innehåller en rad per frågeuttryck i den cachelagrade planen och radernas livslängd är kopplad till själva planen. När en plan tas bort från cachen tas motsvarande rader bort från den här vyn. Om en fråga inte har någon cachelagrad plan, till exempel eftersom OPTION (RECOMPILE) används, finns den inte i resultatet från den här vyn.

Hitta de vanligaste frågorna efter CPU-tid

I följande exempel returneras information om de 15 vanligaste frågorna rangordnade efter genomsnittlig CPU-tid per körning. Det här exemplet aggregerar frågorna enligt deras frågehash, så att logiskt likvärdiga frågor grupperas efter deras kumulativa resursförbrukning.

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

Övervaka frågeplaner för kumulativ CPU-tid

En ineffektiv frågeplan kan också öka CPU-förbrukningen. I följande exempel avgörs vilken fråga som använder den mest kumulativa processorn i den senaste historiken.

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;

Övervaka blockerade frågor

Långsamma eller långvariga frågor kan bidra till överdriven resursförbrukning och vara en följd av blockerade frågor. Orsaken till blockeringen kan vara dålig programdesign, dåliga frågeplaner, bristen på användbara index och så vidare.

Du kan använda vyn sys.dm_tran_locks för att hämta information om den aktuella låsningsaktiviteten i databasen. Kodexempel finns i sys.dm_tran_locks. Mer information om felsökning av blockering finns i Förstå och åtgärda Azure SQL-blockeringsproblem.

Övervaka dödlägen

I vissa fall kan två eller flera frågor blockera varandra, vilket resulterar i ett dödläge.

Du kan skapa en Extended Events-spår för att samla in dödlägeshändelser och sedan hitta relaterade förfrågningar och deras exekveringsplaner i Query Store. Läs mer i Analysera och förhindra dödlägen i Azure SQL Database, inklusive ett labb för att Orsaka ett dödläge i AdventureWorksLT. Läs mer om de typer av resurser som kan orsaka attlåser sig.

Behörigheter

I Azure SQL Database, beroende på beräkningsstorlek, distributionsalternativ och data i DMV, kan det krävas antingen VIEW DATABASE STATE, VIEW SERVER PERFORMANCE STATE, eller VIEW SERVER SECURITY STATE behörighet för att köra frågor mot en DMV. De två sista behörigheterna ingår i behörigheten VIEW SERVER STATE. Behörigheter för att visa servertillstånd beviljas via medlemskap i motsvarande serverroller. Information om vilka behörigheter som krävs för att fråga en specifik DMV finns i Dynamiska hanteringsvyer och hitta artikeln som beskriver DMV.

Om du vill ge VIEW DATABASE STATE behörighet till en databasanvändare kör du följande fråga och ersätter database_user med namnet på användarens huvudnamn i databasen:

GRANT VIEW DATABASE STATE TO [database_user];

Om du vill bevilja medlemskap i ##MS_ServerStateReader## serverrollen till en inloggning med namnet login_name på en logisk serveransluter du till master-databasen och kör sedan följande fråga som ett exempel:

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

Det kan ta några minuter innan behörighetsbidraget börjar gälla. Mer information finns i Begränsningar för roller på servernivå.