使用動態管理檢視來監視 Microsoft Azure SQL 受控執行個體的效能
適用於:Azure SQL 受控執行個體
Microsoft Azure SQL 受控執行個體可使用動態管理檢視 (DMVs) 的子集,來診斷可能因為封鎖或長時間執行的查詢、資源瓶頸、不佳的查詢計畫等等所造成的效能問題。 本文提供如何使用動態管理檢視來偵測常見效能問題的相關資訊。
本文是關於 Azure SQL 受控執行個體,另請參閱使用動態管理檢視來監視 Microsoft Azure SQL Database 的效能。
權限
在 Azure SQL 受控執行個體中,查詢動態管理檢視需要VIEW SERVER STATE 權限。
GRANT VIEW SERVER STATE TO database_user;
在 SQL Server 的執行個體和 Azure SQL 受控執行個體中,動態管理檢視會傳回伺服器狀態資訊。
識別 CPU 效能問題
如果 CPU 耗用量長的時間高於 80%,請考慮下列疑難排解步驟:
CPU 問題現在正在發生
如果正在發生問題,有兩個可能的案例:
許多累積耗用大量 CPU 的個別查詢
使用下列查詢識別常見的查詢雜湊:
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;
耗用 CPU 的長時間執行的查詢仍在執行
使用下列查詢識別下列查詢:
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 問題
如果問題發生在過去,而且您想要執行根本原因分析,請使用查詢存放區。 具有資料庫存取權的使用者可以使用 T-SQL 查詢「查詢存放區」資料。 「查詢存放區」預設組態使用 1 小時的細微性。 使用下列查詢查看耗用大量 CPU 查詢的活動。 此查詢會傳回前 15 個耗用 CPU 的查詢。 請務必變更 rsi.start_time >= DATEADD(hour, -2, GETUTCDATE()
:
-- Top 15 CPU consuming queries by query hash
-- note that a query hash can have many query id if not parameterized or not parameterized properly
-- it grabs a sample query text by min
WITH AggregatedCPU AS (SELECT q.query_hash, SUM(count_executions * avg_cpu_time / 1000.0) AS total_cpu_millisec, SUM(count_executions * avg_cpu_time / 1000.0)/ SUM(count_executions) AS avg_cpu_millisec, MAX(rs.max_cpu_time / 1000.00) AS max_cpu_millisec, 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
JOIN sys.query_store_query AS q ON qt.query_text_id=q.query_text_id
JOIN sys.query_store_plan AS p ON q.query_id=p.query_id
JOIN sys.query_store_runtime_stats AS rs ON rs.plan_id=p.plan_id
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_millisec, avg_cpu_millisec, max_cpu_millisec, 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_millisec DESC, query_hash ASC) AS RN
FROM AggregatedCPU)
SELECT OD.query_hash, OD.total_cpu_millisec, OD.avg_cpu_millisec, OD.max_cpu_millisec, 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.RN
FROM OrderedCPU AS OD
WHERE OD.RN<=15
ORDER BY total_cpu_millisec DESC;
一旦您識別有問題的查詢之後,就可以調整這些查詢,以減少 CPU 使用率。 如果您沒有時間調整查詢,也可以選擇升級受控執行個體的 SLO,以解決此問題。
識別 IO 效能問題
識別 IO 效能問題時,與 IO 問題相關聯的常見等候類型為:
PAGEIOLATCH_*
資料檔案 IO 問題 (包括
PAGEIOLATCH_SH
、PAGEIOLATCH_EX
、PAGEIOLATCH_UP
)。 如果等候類型名稱中有 IO,則指向 IO 問題。 如果在頁面閂鎖等候名稱中沒有任何 IO,就會指向不同類型的問題 (例如,tempdb
競爭)。WRITE_LOG
交易記錄 IO 問題。
如果正在發生 IO 問題
使用 sys.dm_exec_requests 或 sys.dm_os_waiting_tasks 查看 wait_type
和 wait_time
。
使用「查詢存放區」檢視與緩衝區相關的 IO
對於選項 2,您可以針對「查詢存放區」,將下列查詢用於與緩衝區相關的 IO,以檢視過去兩個小時追蹤的活動:
-- 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
JOIN sys.query_store_query AS q ON qt.query_text_id=q.query_text_id
JOIN sys.query_store_plan AS p ON q.query_id=p.query_id
JOIN sys.query_store_wait_stats AS waits ON waits.plan_id=p.plan_id
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 RN
FROM Aggregated)
SELECT OD.query_hash, OD.total_executions, OD.total_wait_time_ms, OD.sampled_query_text, OD.wait_category_desc, OD.RN
FROM Ordered AS OD
WHERE OD.RN<=15
ORDER BY total_wait_time_ms DESC;
GO
檢視 WRITELOG 等候的總記錄 IO
如果等候類型是 WRITELOG
,使用下列查詢可依陳述式檢視總記錄 IO:
-- 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_millisec,
SUM(count_executions * avg_cpu_time / 1000.0) / SUM(count_executions) AS avg_cpu_millisec,
SUM(count_executions * avg_log_bytes_used) AS total_log_bytes_used,
MAX(rs.max_cpu_time / 1000.00) AS max_cpu_millisec,
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
JOIN sys.query_store_query AS q
ON qt.query_text_id = q.query_text_id
JOIN sys.query_store_plan AS p
ON q.query_id = p.query_id
JOIN sys.query_store_runtime_stats AS rs
ON rs.plan_id = p.plan_id
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 RN
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.RN
FROM OrderedLogUsed AS OD
WHERE OD.RN <= 15
ORDER BY total_log_bytes_used DESC;
GO
識別 tempdb
效能問題
識別 IO 效能問題時,與 tempdb
問題相關聯的常見等候類型為 PAGELATCH_*
(而非 PAGEIOLATCH_*
)。 不過,PAGELATCH_*
等候不一定表示您有 tempdb
爭用。 這個等候也表示您有使用者物件資料頁面爭用,因為針對相同的資料頁面進行並行要求。 若要進一步確認 tempdb
競爭,請使用 sys.dm_exec_requests 確認 wait_resource 值開頭為 2:x:y
,其中 2 是 tempdb
資料庫識別碼、x
是檔案識別碼,而 y
是分頁識別碼。
對於 tempdb
競爭,常見的方法是減少或重新撰寫依賴 tempdb
的應用程式程式碼。 常見的 tempdb
使用區域包括:
- 暫存資料表
- 資料表變數
- 資料表值參數
- 版本存放區使用量 (與長時間執行的交易有關)
- 具有使用排序、雜湊聯結和多工緩衝處理之查詢計劃的查詢
使用資料表變數和暫存資料表的常見查詢
使用下列查詢識別使用資料表變數和暫存資料表的常見查詢:
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)') 'Database', stmt.stmt_details.value('@Schema', 'varchar(max)') 'Schema', stmt.stmt_details.value('@Table', 'varchar(max)') '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
JOIN #tmpPlan AS t2 ON t.plan_handle=t2.plan_handle;
識別長時間執行的交易
使用下列查詢識別長時間執行的交易。 長時間執行的交易可防止版本存放區清除。
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;
識別記憶體授與等候效能問題
如果您的常見等候類型為 RESOURCE_SEMAHPORE
,而且沒有高 CPU 使用量問題,則可能有記憶體授與等候問題。
判斷 RESOURCE_SEMAHPORE
等候是否為常見等候
使用下列查詢判斷 RESOURCE_SEMAHPORE
等候是否為常見等候
SELECT wait_type,
SUM(wait_time) AS total_wait_time_ms
FROM sys.dm_exec_requests AS req
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;
識別高記憶體耗用的陳述式
如果您發生記憶體不足的錯誤,請檢閱 sys.dm_os_out_of_memory_events。
使用下列查詢識別高記憶體耗用陳述式:
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
JOIN sys.query_store_runtime_stats AS r
ON p.plan_id = r.plan_id
JOIN sys.query_store_runtime_stats_interval AS i
ON r.runtime_stats_interval_id = i.runtime_stats_interval_id
WHERE start_time > '2018-10-11 14:00:00.0000000'
AND end_time < '2018-10-17 20:00:00.0000000';
GO
;WITH cte
AS (SELECT query_id,
query_plan,
m.c.value('@SerialDesiredMemory', 'INT') AS SerialDesiredMemory
FROM #tmp AS t
CROSS APPLY t.query_plan.nodes('//*:MemoryGrantInfo[@SerialDesiredMemory[. > 0]]') AS m(c) )
SELECT TOP 50
cte.query_id,
t.query_sql_text,
cte.query_plan,
CAST(SerialDesiredMemory / 1024. AS DECIMAL(10, 2)) SerialDesiredMemory_MB
FROM cte
JOIN sys.query_store_query AS q
ON cte.query_id = q.query_id
JOIN sys.query_store_query_text AS t
ON q.query_text_id = t.query_text_id
ORDER BY SerialDesiredMemory DESC;
識別記憶體授與
使用下列查詢識別前 10 個有效記憶體授與:
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),
' '
)
) 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
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;
計算資料庫和物件的大小
下列查詢會傳回資料庫的大小 (以 MB 為單位):
-- Calculates the size of the database.
SELECT SUM(CAST(FILEPROPERTY(name, 'SpaceUsed') AS bigint) * 8192.) / 1024 / 1024 AS DatabaseSizeInMB
FROM sys.database_files
WHERE type_desc = 'ROWS';
GO
下列查詢會傳回您資料庫中個別物件的大小 (以 MB 為單位):
-- Calculates the size of individual database objects.
SELECT sys.objects.name, SUM(reserved_page_count) * 8.0 / 1024
FROM sys.dm_db_partition_stats, sys.objects
WHERE sys.dm_db_partition_stats.object_id = sys.objects.object_id
GROUP BY sys.objects.name;
GO
監視連線
您可以使用 sys.dm_exec_connections 檢視,以擷取對特定受控執行個體所建立連線的相關資訊,還有每個連線的詳細資料。 此外,sys.dm_exec_sessions 檢視有助於擷取所有作用中使用者的連接資訊和內部工作。
下列查詢可擷取目前的連接資訊:
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
JOIN sys.dm_exec_sessions AS s
ON c.session_id = s.session_id
WHERE c.session_id = @@SPID;
監視資源使用量
您可以使用查詢存放區來監視資源使用量,就像在 SQL Server 中一樣。
您也可以使用 sys.dm_db_resource_stats 和 sys.server_resource_stats 來監視使用量。
sys.dm_db_resource_stats
您可以在每一個資料庫中使用 sys.dm_db_resource_stats 檢視表。 sys.dm_db_resource_stats
檢視可顯示相對於服務層級的最新資源使用量資料。 每隔 15 秒鐘就會記錄一次 CPU、資料 IO、記錄檔寫入和記憶體的平均百分比,並且會維持 1 小時。
因為此檢視會提供更細微的資源使用量資訊,請先使用 sys.dm_db_resource_stats
來進行任何現狀分析或疑難排解。 例如,下列查詢會顯示目前的資料庫在過去一小時的平均和最大資源使用量:
SELECT
AVG(avg_cpu_percent) AS 'Average CPU use in percent',
MAX(avg_cpu_percent) AS 'Maximum CPU use in percent',
AVG(avg_data_io_percent) AS 'Average data IO in percent',
MAX(avg_data_io_percent) AS 'Maximum data IO in percent',
AVG(avg_log_write_percent) AS 'Average log write use in percent',
MAX(avg_log_write_percent) AS 'Maximum log write use in percent',
AVG(avg_memory_usage_percent) AS 'Average memory use in percent',
MAX(avg_memory_usage_percent) AS 'Maximum memory use in percent'
FROM sys.dm_db_resource_stats;
如需其他查詢的資訊,請參閱 sys.dm_db_resource_stats 中的範例。
sys.server_resource_stats
您可以使用 sys.server_resource_stats 來傳回 Azure SQL 受控執行個體的 CPU 使用率、IO 和儲存體資料。 於五分鐘間隔內收集及彙總資料。 每 15 秒各報告一個資料列。 傳回的資料包括 CPU 使用率、儲存體大小、IO 使用率和受控執行個體 SKU。 歷程記錄資料大約會保留 14 天。
下列範例顯示以不同方式使用 sys.server_resource_stats
目錄檢視,以取得執行個體如何使用資源的相關資訊。
下列範例會傳回過去七天內的平均 CPU 使用量:
DECLARE @s datetime; DECLARE @e datetime; SET @s= DateAdd(d,-7,GetUTCDate()); SET @e= GETUTCDATE(); SELECT AVG(avg_cpu_percent) AS Average_Compute_Utilization FROM sys.server_resource_stats WHERE start_time BETWEEN @s AND @e; GO
下列範例會傳回執行個體每天使用的平均儲存空間,以便進行成長趨勢分析:
DECLARE @s datetime; DECLARE @e datetime; SET @s= DateAdd(d,-7,GetUTCDate()); SET @e= GETUTCDATE(); SELECT Day = convert(date, start_time), AVG(storage_space_used_mb) AS Average_Space_Used_mb FROM sys.server_resource_stats WHERE start_time BETWEEN @s AND @e GROUP BY convert(date, start_time) ORDER BY convert(date, start_time); GO
並行要求數上限
若要查看目前的並行要求數目,請在資料庫上執行下列 Transact-SQL 查詢:
SELECT COUNT(*) AS [Concurrent_Requests]
FROM sys.dm_exec_requests R;
若要分析個別資料庫的工作負載,請修改此查詢來篩選您要分析的特定資料庫。 比方說,如果您擁有名為 MyDatabase
的資料庫,則下列 Transact-SQL 查詢會傳回該資料庫中並行要求的計數:
SELECT COUNT(*) AS [Concurrent_Requests]
FROM sys.dm_exec_requests R
INNER JOIN sys.databases D ON D.database_id = R.database_id
AND D.name = 'MyDatabase';
這只是單一時間點的快照。 若要進一步了解您的工作負載和並行要求需求,您必須收集一段時間內的許多範例。
並行登入數上限
您可以分析您的使用者和應用程式模式以了解登入頻率。 您也可以在測試環境中執行真實世界的負載,藉此確定您不會達到我們在本文中討論的這項限制或其他限制。 單一查詢或動態管理檢視 (DMV) 無法顯示並行登入計數或歷程記錄。
如果這些用戶端使用相同的連接字串,服務仍會驗證每一個登入。 如果有 10 位使用者同時以相同的使用者名稱和密碼連接到資料庫,將會有 10 個並行登入。 這項限制只適用於登入和驗證期間。 如果相同的 10 位使用者依序連接到資料庫,並行登入數目絕對不會大於 1。
工作階段數上限
若要查看目前的作用中工作階段數目,請在資料庫上執行下列 Transact-SQL 查詢:
SELECT COUNT(*) AS [Sessions]
FROM sys.dm_exec_connections;
如果您分析 SQL Server 工作負載,請修改查詢以專注於特定資料庫。 如果您考慮將資料庫移至 Azure,此查詢可協助您判斷該資料庫可能的工作階段需求。
SELECT COUNT(*) AS [Sessions]
FROM sys.dm_exec_connections C
INNER JOIN sys.dm_exec_sessions S ON (S.session_id = C.session_id)
INNER JOIN sys.databases D ON (D.database_id = S.database_id)
WHERE D.name = 'MyDatabase';
同樣地,這些查詢傳回的是某一時間點的計數。 如果您在一段時間內收集多個樣本,就能充分了解工作階段使用量。
監視查詢效能
速度慢或長時間執行的查詢可能會耗用大量系統資源。 本節示範如何使用動態管理檢視偵測幾個常見的查詢效能問題。
尋找前 N 個查詢
下列範例會傳回以平均 CPU 時間排名的前五個查詢的相關資訊。 此範例會根據查詢雜湊來彙總查詢,使在邏輯上等同的查詢依其累積資源耗用量進行分組。
SELECT TOP 5 query_stats.query_hash AS "Query Hash",
SUM(query_stats.total_worker_time) / SUM(query_stats.execution_count) AS "Avg CPU Time",
MIN(query_stats.statement_text) AS "Statement Text"
FROM
(SELECT QS.*,
SUBSTRING(ST.text, (QS.statement_start_offset/2) + 1,
((CASE statement_end_offset
WHEN -1 THEN DATALENGTH(ST.text)
ELSE QS.statement_end_offset END
- QS.statement_start_offset)/2) + 1) AS statement_text
FROM sys.dm_exec_query_stats AS QS
CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) as ST) as query_stats
GROUP BY query_stats.query_hash
ORDER BY 2 DESC;
監視封鎖的查詢
速度慢或長時間執行的查詢會造成過度的資源耗用,並且會導致封鎖查詢的後果。 導致封鎖的原因可能是不佳的應用程式設計、不良的查詢計畫、缺乏有用的索引等等。 您可以使用 sys.dm_tran_locks 檢視表來取得資料庫中目前鎖定活動的相關資訊。 如需範例程式碼,請參閱 sys.dm_tran_locks。 如需針對封鎖進行疑難排解的詳細資訊,請參閱了解和解決 Azure SQL Database 封鎖問題。
監視鎖死
在某些情況下,兩個或以上的查詢可能會彼此相互封鎖,導致鎖死。
您可以建立擴充事件來追蹤資料庫,以擷取鎖死事件,接著在查詢存放區中尋找相關的查詢及其執行計畫。
針對 Azure SQL 受控執行個體,請參閱鎖死指南中的鎖死工具。
監視查詢計畫
效率不佳的查詢計畫也可能會增加 CPU 耗用量。 下列範例使用 sys.dm_exec_query_stats 檢視來判斷哪一個查詢使用最多的累計 CPU。
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 50
qs.plan_handle,
qs.total_worker_time
FROM
sys.dm_exec_query_stats 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;
其他監視選項
使用資料庫監看員進行監視 (預覽版)
資料庫監看員會收集深入的工作負載監視資料,為您提供資料庫效能、組態和健康情況的詳細資料檢視。 Azure 入口網站中的儀表板提供 Azure SQL 資產的單一窗格檢視,以及每個監視資源的詳細資料檢視。 資料會被收集到 Azure 訂用帳戶中的中央資料存放區。 可查詢、分析、匯出、視覺化收集的資料,並將其與下游系統整合。
如需資料庫監看員的詳細資訊,請參閱下列文章:
- 使用資料庫監看員監視 Azure SQL 工作負載 (預覽版)
- 快速入門:建立資料庫監看員以監視 Azure SQL (預覽版)
- 建立及設定資料庫監看員 (預覽版)
- 資料庫監看員資料收集和資料集 (預覽版)
- 分析資料庫監看員監視資料 (預覽版)
- 資料庫監看員常見問題
使用 Azure 監視器進行監視
Azure 監視器提供了各種診斷資料收集群組、計量和端點,以監視 Azure SQL 受控執行個體。 如需詳細資訊,請參閱使用 Azure 監視器監視 Azure SQL 受控執行個體。 Azure SQL 分析 (預覽) 可與 Azure 監視器整合,其中許多監視解決方案不再處於開發中狀態。 如需更多監視選項,請參閱 Azure SQL 受控執行個體和 Azure SQL Database 中的監視和效能微調。