Monitorowanie wydajności przy użyciu dynamicznych widoków zarządzania

Dotyczy:Azure SQL DatabaseBaza danych SQL w usłudze Fabric

Dynamiczne widoki zarządzania (DMV) można wykonywać zapytania za pośrednictwem Transact-SQL (T-SQL), aby monitorować wydajność obciążeń i diagnozować problemy z wydajnością, które mogą być spowodowane zablokowanymi lub długotrwałymi zapytaniami, wąskimi gardłami zasobów, nieoptymalnymi planami zapytań i nie tylko.

W przypadku graficznego monitorowania zasobów zapytań użyj magazynu zapytań.

Wskazówka

Wypróbuj Database Hub w Microsoft Fabric! Centrum baz danych:

  • automatycznie wykrywa Twoje bazy danych w Azure, Microsoft Fabric, lokalnie oraz obsługiwanych środowiskach multicloud.
  • Automatycznie zapewnia gotowe, konfigurowalne pulpity nawigacyjne, które można udostępniać.
  • Automatycznie identyfikuje najważniejsze priorytety i rekomendacje w Twoich bazach danych, które powinieneś zbadać lub poprawić.
  • jest całkowicie darmowa.

Więcej informacji można znaleźć w Database Hub w Microsoft Fabric.

Monitorowanie użycia zasobów

Użycie zasobów można monitorować na poziomie bazy danych przy użyciu następujących widoków DMV.

sys.dm_db_resource_stats

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

SELECT DB_NAME() AS database_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;

Widok sys.dm_db_resource_stats pokazuje ostatnie dane użycia zasobów w stosunku do limitów rozmiaru obliczeniowego. Wartości procentowe wykorzystania procesora CPU, operacji we/wy danych, zapisów dziennika, wątków roboczych i pamięci względem limitu są rejestrowane w każdym 15-sekundowym przedziale i przechowywane przez około godzinę.

Aby zapoznać się z innymi przykładowymi zapytaniami, zobacz przykłady w sys.dm_db_resource_stats.

sys.resource_stats

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

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

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

Na podstawie danych ta baza danych ma obecnie szczytowe obciążenie CPU wynoszące nieco ponad 50% w odniesieniu do warstwy obliczeniowej P2 (we wtorek w południe). Jeśli CPU jest dominującym czynnikiem w profilu wykorzystania zasobów aplikacji, możesz zdecydować, że P2 jest odpowiednim rozmiarem jednostki obliczeniowej, aby zagwarantować, że obciążenie zawsze zmieści się w dostępnych zasobach. Jeśli spodziewasz się, że aplikacja wzrośnie wraz z upływem czasu, dobrym pomysłem jest posiadanie dodatkowego buforu zasobów, aby aplikacja nigdy nie osiągnęła limitu poziomu wydajności. Jeśli zwiększysz rozmiar obliczeniowy, możesz uniknąć widocznych przez klienta błędów, które mogą wystąpić, gdy baza danych nie ma wystarczającej mocy do efektywnego przetwarzania żądań, szczególnie w środowiskach wrażliwych na opóźnienia.

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

Silnik bazy danych udostępnia informacje o wykorzystaniu zasobów dla każdej aktywnej bazy danych w widoku sys.resource_stats bazy danych master na każdym serwerze logicznym. Dane w widoku są agregowane przez 5-minutowe interwały. Wyświetlenie tych danych w tabeli może potrwać kilka minut, więc sys.resource_stats jest bardziej przydatne w przypadku analizy historycznej, a nie analizy niemal w czasie rzeczywistym. sys.resource_stats Wykonaj zapytanie względem widoku, aby wyświetlić najnowszą historię bazy danych i sprawdzić, czy wybrany rozmiar obliczeniowy dostarczył odpowiednią wydajność w razie potrzeby.

Uwaga

Aby wykonać zapytanie master w poniższych przykładach, musisz mieć połączenie z bazą sys.resource_stats danych.

W tym przykładzie przedstawiono dane w pliku sys.resource_stats:

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

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

  1. Aby sprawdzić wykorzystanie zasobów z ostatniego tygodnia dla bazy danych użytkownika userdb1, możesz uruchomić następujące zapytanie, podstawiając własną nazwę bazy danych:

    SELECT *
    FROM sys.resource_stats
    WHERE database_name = 'userdb1'
          AND start_time > DATEADD(day, -7, GETDATE())
    ORDER BY start_time DESC;
    
  2. Aby ocenić, jak dobrze obciążenie pasuje do rozmiaru obliczeniowego, należy przejść do szczegółów poszczególnych aspektów metryk zasobów: procesora CPU, operacji we/wy danych, zapisu dziennika, liczby procesów roboczych i liczby sesji. Oto zmienione zapytanie używające sys.resource_stats do raportowania średnich i maksymalnych wartości tych metryk zasobów dla każdego rozmiaru mocy obliczeniowej, dla którego skonfigurowano bazę danych:

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

    • W przypadku baz danych w modelu zakupu DTU:

      Na przykład możesz używać warstwy usługi Standard w rozmiarze obliczeniowym S2. Średnie wykorzystanie procesora CPU oraz średni procent operacji odczytu i zapisu we/wy są poniżej 40%, średnia liczba procesów roboczych jest niższa niż 50, a średnia liczba sesji jest niższa niż 200. Twoje obciążenie robocze może pasować do warstwy mocy obliczeniowej S1. Łatwo sprawdzić, czy baza danych mieści się w limitach procesów roboczych i sesji. Aby sprawdzić, czy baza danych mieści się w niższym rozmiarze obliczeniowym, podziel liczbę jednostek DTU mniejszego rozmiaru obliczeniowego na liczbę jednostek DTU bieżącego rozmiaru obliczeniowego, a następnie pomnoż wynik o 100:

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

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

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

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

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

      Średnie użycie procesora CPU (%) Maksymalny procent procesora CPU
      24.5 100.00

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

    • W przypadku baz danych w modelu zakupowym DTU i modelu zakupowym vCore:

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

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

      Te wartości procentowe oznaczają, jaki odsetek próbek przypadał na obciążenie mieszczące się w ramach bieżącego rozmiaru zasobów obliczeniowych. Jeśli to zapytanie zwróci wartość mniejszą niż 99,9 procent dla dowolnego z trzech wymiarów zasobów, średnie obciążenie próbki przekroczyło limity. Rozważ przejście do następnego wyższego rozmiaru obliczeniowego lub użycie technik dostrajania aplikacji w celu zmniejszenia obciążenia bazy danych.

sys.dm_elastic_pool_resource_stats

Dotyczy tylko:Azure SQL Database

Podobnie jak sys.dm_db_resource_stats, sys.dm_elastic_pool_resource_stats udostępnia ostatnie i szczegółowe dane dotyczące użycia zasobów elastycznej puli usługi Azure SQL Database. Do widoku można wysyłać zapytania z dowolnej bazy danych w puli elastycznej w celu uzyskania danych o użyciu zasobów dla całej puli, a nie dla konkretnej bazy danych. Wartości procentowe zgłaszane przez ten DMV odnoszą się do limitów puli elastycznej, które mogą być wyższe niż limity dla bazy danych należącej do tej puli.

W tym przykładzie przedstawiono podsumowane dane użycia zasobów dla bieżącej elastycznej puli w ciągu ostatnich 15 minut:

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;

Jeśli okaże się, że każde użycie zasobów zbliża się do 100% przez dłuższy czas, może być konieczne przejrzenie użycia zasobów dla poszczególnych baz danych w tej samej elastycznej puli, aby określić, ile każda baza danych przyczynia się do użycia zasobów na poziomie puli.

sys.elastic_pool_resource_stats

Dotyczy tylko:Azure SQL Database

Podobnie jak sys.resource_stats, sys.elastic_pool_resource_stats w bazie danych master udostępnia historyczne dane o użyciu zasobów dla wszystkich pul elastycznych na serwerze logicznym. Za pomocą sys.elastic_pool_resource_stats można monitorować dane historyczne z ostatnich 14 dni, w tym analizować trendy użycia.

W tym przykładzie przedstawiono podsumowane dane użycia zasobów w ciągu ostatnich siedmiu dni dla wszystkich pul elastycznych na bieżącym serwerze logicznym. Wykonaj zapytanie w master bazie danych.

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;

Żądania współbieżne

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

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

To tylko chwilowy obraz sytuacji w danym momencie. Aby lepiej zrozumieć wymagania dotyczące Twojego obciążenia i współbieżnych żądań, musisz zbierać wiele próbek na przestrzeni czasu.

Średnia szybkość żądań

W tym przykładzie pokazano, jak znaleźć średni współczynnik żądań dla bazy danych lub baz danych w elastycznej puli w danym okresie. W tym przykładzie okres jest ustawiony na 30 sekund. Można ją dostosować, modyfikując instrukcję WAITFOR DELAY . Wykonaj to zapytanie w bazie danych użytkownika. Jeśli baza danych znajduje się w elastycznej puli i jeśli masz wystarczające uprawnienia, wyniki obejmują inne bazy danych w elastycznej puli.

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;

Bieżące sesje

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

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

To zapytanie zwraca liczbę punktów w czasie. Jeśli z czasem zbierzesz wiele próbek, najlepiej zrozumiesz sposób korzystania z sesji.

Najnowsza historia żądań, sesji i procesów roboczych

Ten przykład zwraca niedawne dane historyczne dotyczące użycia żądań, sesji i wątków roboczych dla bazy danych lub baz danych należących do puli elastycznej. Każdy wiersz reprezentuje migawkę użycia zasobów w danym momencie dla bazy danych. Kolumna requests_per_second jest średnią szybkością żądań w przedziale czasu kończącym się na .snapshot_time Jeśli baza danych znajduje się w elastycznej puli i jeśli masz wystarczające uprawnienia, wyniki obejmują inne bazy danych w elastycznej puli.

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;

Obliczanie rozmiarów baz danych i obiektów

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

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

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

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

Identyfikowanie problemów z wydajnością procesora CPU

Ta sekcja ułatwia identyfikowanie poszczególnych zapytań, które są głównymi użytkownikami procesora CPU.

Wskazówka

Rozważ automatyczne dostrajanie bazy danych , aby automatycznie zwiększyć wydajność zapytań.

Jeśli użycie procesora CPU przekracza 80% przez dłuższy czas, rozważ następujące kroki rozwiązywania problemów, czy problem z procesorem CPU występuje teraz , czy wystąpił w przeszłości. Możesz również wykonać kroki opisane w tej sekcji, aby aktywnie identyfikować zapytania zużywające najwięcej procesora CPU i dostroić je. W niektórych przypadkach zmniejszenie użycia procesora może umożliwić zmniejszenie rozmiaru baz danych i pul elastycznych oraz obniżenie kosztów.

Kroki rozwiązywania problemów są takie same w przypadku autonomicznych baz danych i baz danych w elastycznej puli. Wykonaj wszystkie zapytania w bazie danych użytkownika.

Problem z CPU występuje obecnie

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

Wiele pojedynczych zapytań, które łącznie powodują wysokie użycie procesora CPU

Użyj następującego zapytania, aby zidentyfikować najważniejsze zapytania według skrótu zapytania:

PRINT '-- top 10 Active CPU Consuming Queries (aggregated)--';
SELECT TOP 10 GETDATE() AS runtime,
              *
FROM (SELECT query_stats.query_hash,
             SUM(query_stats.cpu_time) AS 'Total_Request_Cpu_Time_Ms',
             SUM(logical_reads) AS 'Total_Request_Logical_Reads',
             MIN(start_time) AS 'Earliest_Request_start_Time',
             COUNT(*) AS 'Number_Of_Requests',
             SUBSTRING(REPLACE(REPLACE(MIN(query_stats.statement_text), CHAR(10), ' '), CHAR(13), ' '), 1, 256) AS "Statement_Text"
      FROM (SELECT req.*,
                   SUBSTRING(ST.text, (req.statement_start_offset / 2) + 1, ((CASE statement_end_offset WHEN -1 THEN DATALENGTH(ST.text) ELSE req.statement_end_offset END - req.statement_start_offset) / 2) + 1) AS statement_text
            FROM sys.dm_exec_requests AS req
                CROSS APPLY sys.dm_exec_sql_text(req.sql_handle) AS ST) AS query_stats
      GROUP BY query_hash) AS t
ORDER BY Total_Request_Cpu_Time_Ms DESC;

Długotrwałe zapytania, które zużywają procesor CPU, są nadal uruchomione

Użyj następującego zapytania, aby zidentyfikować te zapytania:

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

Problem z CPU wystąpił wcześniej

Jeśli problem wystąpił w przeszłości i chcesz przeprowadzić analizę przyczyn źródłowych, użyj Query Store. Użytkownicy z dostępem do bazy danych mogą używać języka T-SQL do wykonywania zapytań dotyczących danych magazynu zapytań. Domyślnie magazyn zapytań przechwytuje zagregowane statystyki zapytań dla interwałów jednogodzinnych.

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

    -- 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) AS max_logical_reads,
               COUNT(DISTINCT p.plan_id) AS number_of_distinct_plans,
               COUNT(DISTINCT p.query_id) AS number_of_distinct_query_ids,
               SUM(CASE WHEN rs.execution_type_desc = 'Aborted' THEN count_executions ELSE 0 END) AS Aborted_Execution_Count,
               SUM(CASE WHEN rs.execution_type_desc = 'Regular' THEN count_executions ELSE 0 END) AS Regular_Execution_Count,
               SUM(CASE WHEN rs.execution_type_desc = 'Exception' THEN count_executions ELSE 0 END) AS Exception_Execution_Count,
               SUM(count_executions) AS total_executions,
               MIN(qt.query_sql_text) AS sampled_query_text
        FROM sys.query_store_query_text AS qt
             INNER JOIN sys.query_store_query AS q
                 ON qt.query_text_id = q.query_text_id
             INNER JOIN sys.query_store_plan AS p
                 ON q.query_id = p.query_id
             INNER JOIN sys.query_store_runtime_stats AS rs
                 ON rs.plan_id = p.plan_id
             INNER JOIN sys.query_store_runtime_stats_interval AS rsi
                 ON rsi.runtime_stats_interval_id = rs.runtime_stats_interval_id
        WHERE rs.execution_type_desc IN ('Regular', 'Aborted', 'Exception')
              AND rsi.start_time >= DATEADD(HOUR, -2, GETUTCDATE())
        GROUP BY q.query_hash),
     OrderedCPU
    AS (SELECT query_hash,
               total_cpu_ms,
               avg_cpu_ms,
               max_cpu_ms,
               max_logical_reads,
               number_of_distinct_plans,
               number_of_distinct_query_ids,
               total_executions,
               Aborted_Execution_Count,
               Regular_Execution_Count,
               Exception_Execution_Count,
               sampled_query_text,
               ROW_NUMBER() OVER (ORDER BY total_cpu_ms DESC, query_hash ASC) AS query_hash_row_number
        FROM AggregatedCPU)
    SELECT OD.query_hash,
           OD.total_cpu_ms,
           OD.avg_cpu_ms,
           OD.max_cpu_ms,
           OD.max_logical_reads,
           OD.number_of_distinct_plans,
           OD.number_of_distinct_query_ids,
           OD.total_executions,
           OD.Aborted_Execution_Count,
           OD.Regular_Execution_Count,
           OD.Exception_Execution_Count,
           OD.sampled_query_text,
           OD.query_hash_row_number
    FROM OrderedCPU AS OD
    WHERE OD.query_hash_row_number <= 15 --get top 15 rows by total_cpu_ms
    ORDER BY total_cpu_ms DESC;
    
  2. Po zidentyfikowaniu problematycznych zapytań nadszedł czas, aby dostosować te zapytania w celu zmniejszenia wykorzystania procesora CPU. Alternatywnie możesz zwiększyć rozmiar obliczeniowy bazy danych lub elastycznej puli, aby obejść ten problem.

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

Zidentyfikuj problemy z wydajnością I/O

Podczas identyfikowania problemów z wydajnością operacji wejścia/wyjścia (I/O) pamięci masowej główne typy oczekiwania to:

  • PAGEIOLATCH_*

    W przypadku problemów z operacjami wejścia/wyjścia pliku danych (w tym PAGEIOLATCH_SH, PAGEIOLATCH_EX, PAGEIOLATCH_UP). Jeśli nazwa typu oczekiwania zawiera IO, wskazuje to na problem z operacjami wejścia/wyjścia. Jeśli w nazwie oczekiwania zatrzasku strony nie ma IO, wskazuje to na inny rodzaj problemu, który nie jest związany z wydajnością magazynu (na przykład rywalizacja).

  • WRITE_LOG

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

Jeśli problem z wejściem/wyjściem występuje teraz

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

Określ użycie wejścia/wyjścia danych i dziennika

Użyj następującego zapytania, aby określić użycie danych i operacji wejścia/wyjścia dziennika.

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

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

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

  • Uaktualnij rozmiar obliczeniowy lub warstwę usługi
  • Zidentyfikuj i dostrój zapytania generujące najwięcej operacji wejścia/wyjścia.

Aby zidentyfikować zapytania z największą liczbą oczekiwań związanych z operacjami we/wy, możesz użyć następującego zapytania Query Store, aby wyświetlić ostatnie dwie godziny rejestrowanej aktywności:

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

Możesz również użyć widoku sys.query_store_runtime_stats , koncentrując się na zapytaniach z dużymi wartościami w kolumnach avg_physical_io_reads i avg_num_physical_io_reads .

Wyświetl łączną liczbę operacji wejścia/wyjścia dziennika dla oczekiwań WRITELOG

Jeśli typ oczekiwania to WRITELOG, użyj następującego zapytania, aby wyświetlić całkowitą liczbę operacji wejścia/wyjścia dziennika dla każdej instrukcji:

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

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

Typowe typy oczekiwania skojarzone z problemami tempdb to PAGELATCH_* (nie PAGEIOLATCH_*). Jednak oczekiwania nie zawsze oznaczają, PAGELATCH_* że masz tempdb rywalizację. To oczekiwanie może też oznaczać, że występuje rywalizacja o stronę danych obiektu użytkownika spowodowana współbieżnymi żądaniami kierowanymi do tej samej strony danych. Aby jeszcze bardziej potwierdzić tempdb rywalizację, użyj sys.dm_exec_requests , aby potwierdzić, że wait_resource wartość zaczyna się od 2:x:y miejsca, gdzie 2 jest tempdb identyfikatorem bazy danych, x jest identyfikatorem pliku i y jest identyfikatorem strony.

W przypadku konfliktów związanych z tempdb typową metodą jest ograniczenie lub przepisanie kodu aplikacji, który opiera się na tempdb. Typowe tempdb obszary użycia obejmują:

  • Tabele tymczasowe
  • Zmienne tabelaryczne
  • Parametry z wartościami przechowywanymi w tabeli
  • Zapytania, których plany wykonania wykorzystują sortowanie, złączenia haszujące i spooli

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

Wszystkie bazy danych w elastycznej puli współdzielą tę samą tempdb bazę danych. Wysokie tempdb wykorzystanie miejsca przez jedną bazę danych może mieć wpływ na inne bazy danych w tej samej elastycznej puli.

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

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

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

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

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

Identyfikowanie długotrwałych transakcji

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

SELECT DB_NAME(dtr.database_id) AS '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 OUTER JOIN sys.dm_tran_session_transactions AS sess
         ON sess.transaction_id = atr.transaction_id
     LEFT OUTER JOIN sys.dm_exec_requests AS req
         ON req.session_id = sess.session_id
        AND req.transaction_id = sess.transaction_id
     LEFT OUTER 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;

Identyfikuj problemy z wydajnością związane z oczekiwaniem na przydział pamięci

Jeśli głównym typem oczekiwania jest RESOURCE_SEMAPHORE, może wystąpić problem z oczekiwaniem na przydział pamięci, w którym zapytania nie mogą rozpocząć wykonywania, dopóki nie uzyskają wystarczająco dużego przydziału pamięci.

Ustal, czy oczekiwanie RESOURCE_SEMAPHORE należy do najczęstszych typów oczekiwań

Użyj następującego zapytania, aby określić, czy oczekiwanie RESOURCE_SEMAPHORE jest głównym typem oczekiwania. Również wzrost pozycji RESOURCE_SEMAPHORE czasu oczekiwania w ostatnim czasie byłby oznaką. Aby uzyskać więcej informacji na temat rozwiązywania problemów z oczekiwaniem na udzielanie pamięci, zobacz Rozwiązywanie problemów z niską wydajnością lub małą ilością pamięci spowodowanych przez przydziały pamięci w programie SQL Server.

SELECT wait_type,
       SUM(wait_time) AS total_wait_time_ms
FROM sys.dm_exec_requests AS req
     INNER JOIN sys.dm_exec_sessions AS sess
         ON req.session_id = sess.session_id
WHERE is_user_process = 1
GROUP BY wait_type
ORDER BY SUM(wait_time) DESC;

Identyfikowanie instrukcji zużywających dużą ilość pamięci

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

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

SELECT IDENTITY (INT, 1, 1) AS rowId,
       CAST (query_plan AS XML) AS 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)) AS SerialDesiredMemory_MB
FROM cte
     INNER JOIN sys.query_store_query AS q
         ON cte.query_id = q.query_id
     INNER JOIN sys.query_store_query_text AS t
         ON q.query_text_id = t.query_text_id
ORDER BY SerialDesiredMemory DESC;

Identyfikowanie 10 aktywnych przydziałów pamięci

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

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

Monitorowanie połączeń

Możesz użyć widoku sys.dm_exec_connections , aby pobrać informacje o połączeniach ustanowionych z określoną bazą danych i szczegóły każdego połączenia. Jeśli baza danych znajduje się w elastycznej puli i masz wystarczające uprawnienia, widok zwraca zestaw połączeń dla wszystkich baz danych w puli elastycznej. Ponadto widok sys.dm_exec_sessions jest przydatny podczas pobierania informacji o wszystkich aktywnych połączeniach użytkowników i zadaniach wewnętrznych.

Wyświetlanie bieżących sesji

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

Wszystkie aktualnie wykonywane sesje w bazie danych są widoczne tylko wtedy, gdy masz uprawnienie VIEW DATABASE STATE do bazy danych podczas wykonywania zapytań względem widoków sys.dm_exec_requests i sys.dm_exec_sessions. W przeciwnym razie zobaczysz tylko bieżącą sesję.

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

Monitorowanie wydajności zapytań

Wolne lub długotrwałe zapytania mogą zużywać znaczne zasoby systemowe. W tej sekcji pokazano, jak używać dynamicznych widoków zarządzania do wykrywania kilku typowych problemów z wydajnością zapytań przy użyciu dynamicznego widoku zarządzania sys.dm_exec_query_stats . Widok zawiera jeden wiersz dla każdej instrukcji zapytania w ramach buforowanego planu, a czas istnienia tych wierszy jest związany z samym planem. Gdy plan zostanie usunięty z pamięci podręcznej, odpowiednie wiersze zostaną wyeliminowane z tego widoku. Jeśli zapytanie nie ma buforowanego planu, na przykład z powodu użycia OPTION (RECOMPILE), nie jest obecne w wynikach tego widoku.

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

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

SELECT TOP 15 query_stats.query_hash AS Query_Hash,
              SUM(query_stats.total_worker_time) / SUM(query_stats.execution_count) AS Avg_CPU_Time,
              MIN(query_stats.statement_text) AS Statement_Text
FROM (SELECT QS.*,
             SUBSTRING(ST.text, (QS.statement_start_offset / 2) + 1, (
             (CASE statement_end_offset
                 WHEN -1 THEN DATALENGTH(ST.text)
                 ELSE QS.statement_end_offset END
              - QS.statement_start_offset) / 2) + 1) AS statement_text
      FROM sys.dm_exec_query_stats AS QS
          CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) AS ST) AS query_stats
GROUP BY query_stats.query_hash
ORDER BY Avg_CPU_Time DESC;

Monitoruj plany zapytań pod kątem łącznego czasu użycia procesora

Nieefektywny plan zapytania może również zwiększyć użycie procesora CPU. Poniższy przykład ustala, które zapytanie zużyło łącznie najwięcej czasu procesora w ostatnim czasie.

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

Monitorowanie zablokowanych zapytań

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

Możesz użyć widoku sys.dm_tran_locks, aby uzyskać informacje o bieżącej aktywności blokowania w bazie danych. Aby zapoznać się z przykładami kodu, zobacz sys.dm_tran_locks. Aby uzyskać więcej informacji na temat rozwiązywania problemów z blokowaniem, zobacz Omówienie i rozwiązywanie problemów z blokowaniem.

Monitoruj zakleszczenia

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

Możesz utworzyć ślad Extended Events w celu przechwytywania zdarzeń zakleszczeń, a następnie znaleźć powiązane zapytania i ich plany wykonywania w Query Store. Więcej informacji znajdziesz w Analizowanie i zapobieganie zakleszczeniom w usłudze Azure SQL Database i SQL Database w Microsoft Fabric. Dowiedz się więcej o zakleszczeniach w przewodniku "Zakleszczenia".

Uprawnienia

W Azure SQL Database, w zależności od rozmiaru obliczeniowego, opcji wdrożenia i danych w DMV, wykonywanie zapytań dotyczących dynamicznego widoku monitorowania może wymagać VIEW DATABASE STATE lub VIEW SERVER PERFORMANCE STATE, albo VIEW SERVER SECURITY STATE uprawnień. Ostatnie dwa uprawnienia są uwzględnione w uprawnieniach VIEW SERVER STATE . Uprawnienia do wyświetlania stanu serwera są przyznawane za pośrednictwem członkostwa w odpowiednich rolach serwera. Aby określić, które uprawnienia są wymagane do wykonywania zapytań dotyczących określonego dynamicznego widoku zarządzania, zobacz Dynamiczne widoki zarządzania systemu i znajdź artykuł opisujący dynamiczny widok zarządzania.

Aby przyznać użytkownikowi bazy danych uprawnienie VIEW DATABASE STATE, uruchom następujące zapytanie, zastępując database_user nazwą podmiotu zabezpieczeń użytkownika w bazie danych:

GRANT VIEW DATABASE STATE TO [database_user];

Aby nadać członkostwo w roli serwera ##MS_ServerStateReader## nazwie logowania login_name na serwerze logicznym, połącz się z bazą danych master, a następnie uruchom przykładowe zapytanie:

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

Może minąć kilka minut, zanim przyznanie uprawnień zacznie obowiązywać. Aby uzyskać więcej informacji, zobacz Ograniczenia ról na poziomie serwera.