Tag Archives: DMV

SQL Query Store: Porovnání dvou období (počty spuštění dotazů, trvání)

Query Store je užitečnou novinkou v Azure SQL, resp. Microsoft SQL Server 2016+. Ukládá užitečné statistiky o spuštěných dotazech, jejich execution planech a mnoho dalšího. (Umí dokonce vynutit používání konkrétního execution planu a dokonce toto řešit automaticky, pokud detekuje výkonovou regresi. O tom ale někdy jindy.)

Jeden ze scénářů, který vám může být užitečný, je porovnání dvou období co se počtu spuštění jednotlivých dotazů týče, jejich průměrné doby trvání, atp. Hodit se to může při nenadálých poklesech výkonnosti, po nasazení nových verzí aplikací, atp.:

DECLARE @Period1Start datetime, @Period1End datetime, @Period2Start datetime, @Period2End datetime

SET @Period1Start = '20171212 00:00'
SET @Period1End = '20171213 00:00'
SET @Period2Start = '20171214 00:00'
SET @Period2End = '20171215 00:00'

;WITH period1 AS
(
	SELECT
		query_id,
		AVG(avg_duration) AS avg_duration,
		SUM(count_executions) AS count_executions_total
	FROM sys.query_store_runtime_stats
		INNER JOIN  sys.query_store_runtime_stats_interval ON (query_store_runtime_stats_interval.runtime_stats_interval_id = query_store_runtime_stats.runtime_stats_interval_id)
		INNER JOIN sys.query_store_plan ON query_store_plan.plan_id = query_store_runtime_stats.plan_id
	WHERE
		(sys.query_store_runtime_stats_interval.start_time >= @Period1Start)
		AND (sys.query_store_runtime_stats_interval.end_time <= @Period1End)
	GROUP BY query_id
),
period2 AS
(
	SELECT
		query_id,
		AVG(avg_duration) AS avg_duration,
		SUM(count_executions) AS count_executions_total
	FROM sys.query_store_runtime_stats
		INNER JOIN  sys.query_store_runtime_stats_interval ON (query_store_runtime_stats_interval.runtime_stats_interval_id = query_store_runtime_stats.runtime_stats_interval_id)
		INNER JOIN sys.query_store_plan ON query_store_plan.plan_id = query_store_runtime_stats.plan_id
	WHERE
		(sys.query_store_runtime_stats_interval.start_time >= @Period2Start)
		AND (sys.query_store_runtime_stats_interval.end_time <= @Period2End)
	GROUP BY query_id
)
SELECT
		query_store_query.query_id,
		period1.avg_duration AS period1_avg_duration,
		period2.avg_duration AS period2_avg_duration,
		CASE WHEN period1.count_executions_total IS NOT NULL THEN (period2.avg_duration - period1.avg_duration) * 100.0 / period1.avg_duration ELSE NULL END AS avg_duration_increase_percent,
		period1.count_executions_total AS period1_count_executions_total,
		period2.count_executions_total AS period2_count_executions_total,
		CASE WHEN period1.count_executions_total IS NOT NULL THEN (period2.count_executions_total - period1.count_executions_total) * 100.0 / period1.count_executions_total ELSE NULL END AS count_execution_increase_percent,
		query_sql_text
	FROM period2
		LEFT JOIN period1 ON period1.query_id = period2.query_id
		LEFT JOIN sys.query_store_query ON query_store_query.query_id = period2.query_id
		LEFT JOIN sys.query_store_query_text ON query_store_query_text.query_text_id = query_store_query.query_text_id
	--ORDER BY period2.count_executions_total DESC
	ORDER BY count_execution_increase_percent DESC
	--ORDER BY avg_duration_increase_percent DESC

References

ASP.NET Performance Tuning – záznam, slides a dema [TechEd 05/2016]

5lides a dema z mé přednášky pro TechEd DevCon Praha ze 19.5.2016:

Záznam z přednášky je publikován na našem HAVIT YouTube Channel.

Dotčená témata

  • ASP.NET Sessions, tinyget.exe
  • PerfView
  • ANTS Performance Profiler
  • String Concatenation – skládání řetězců vs. StringBuilder
  • vyhledávání List<T>, Dictionary<TKey, TValue>, ToLookup(), BinnarySearch()
  • Reflection, dynamic
  • SQL Server Database Management Views [DMV]
  • ASP.NET Data Caching (demo ASP.NET Core)
  • ASP.NET Output Caching (demo ASP.NET Core)

[ASP].NET Performance Tuning – záznam, slides a dema [ShowIT SK 02/2016]

Slides a dema z mé přednášky pro konferenci Gopas ShowIT Bratislava z 9.2.2016:

Záznam z přednášky je publikován na našem HAVIT YouTube Channel.

Dotčená témata

  • ASP.NET Sessions
  • skládání stringů vs. StringBuilder
  • vyhledávání v datech – List vs. BinnarySearch vs. Dictionary vs. LINQ ToLookup()
  • reflexe + dynamic
  • ASP.NET Data Cache + Output Cache
  • SQL DMVs
  • YSlow

SQL DMV: Most Expensive Queries, Missing Indexes

Kolem Database Management Views a jejich využití pro SQL performance diagnostics toho napsáno tuny, ale právě tato kvanta různých zdrojů uvádějí spousty různě kvalitních podob dotazu pro Most Expensive Queries.

Nebaví mě pokaždé hledat ten správný, nebo ho dokonce vymýšlet, proto si zde archivuji mojí oblíbenou podobu:

-- Most expensive queries
SELECT TOP 20
	SUBSTRING(qt.TEXT, (qs.statement_start_offset/2)+1,
		((CASE qs.statement_end_offset
			WHEN -1 THEN DATALENGTH(qt.TEXT)
			ELSE qs.statement_end_offset
			END - qs.statement_start_offset)/2)+1)
		AS query_text,
    db.name AS [db_name],
    qs.total_elapsed_time/1000 AS total_elapsed_time_ms,
    qs.total_elapsed_time/qs.execution_count/1000 AS average_elapsed_time_ms,
    qs.last_elapsed_time/1000 AS last_elapsed_time_ms,
    qs.execution_count,
    qs.total_worker_time/1000 AS total_worker_time_ms,
    qs.total_worker_time/qs.execution_count/1000 AS average_worker_time_ms,
    qs.last_worker_time/1000 AS last_worker_time_ms,
    qs.last_execution_time,
    qs.total_logical_reads,
	qs.last_logical_reads,
    qs.total_logical_writes,
	qs.last_logical_writes
    --qp.query_plan
    FROM sys.dm_exec_query_stats qs
        CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
        CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
        LEFT JOIN sys.databases db ON (qt.dbid = db.database_id)
    -- WHERE db.name='DatabaseName'
    -- ORDER BY qs.total_logical_reads DESC
    -- ORDER BY qs.total_logical_writes DESC
    -- ORDER BY qs.last_worker_time DESC -- CPU time (active)
    -- ORDER BY qs.last_elapsed_time DESC -- clock time (včetně čekání na locky, atp.)
    -- ORDER BY qs.total_worker_time DESC
	ORDER BY qs.total_elapsed_time DESC

…a rovnou přidávám už méně používaný dotaz pro chybějící indexy (varuji před zkratkovitou úvahou, že tyto indexy je nutné přidat):

SELECT
	mid.statement
	  ,migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) * (migs.user_seeks + migs.user_scans) AS improvement_measure,OBJECT_NAME(mid.Object_id),
	  'CREATE INDEX [missing_index_' + CONVERT (VARCHAR, mig.index_group_handle) + '_' + CONVERT (VARCHAR, mid.index_handle)
	  + '_' + LEFT (PARSENAME(mid.statement, 1), 32) + ']'
	  + ' ON ' + mid.statement
	  + ' (' + ISNULL (mid.equality_columns,'')
		+ CASE WHEN mid.equality_columns IS NOT NULL AND mid.inequality_columns IS NOT NULL THEN ',' ELSE '' END
		+ ISNULL (mid.inequality_columns, '')
	  + ')'
	  + ISNULL (' INCLUDE (' + mid.included_columns + ')', '') AS create_index_statement,
	  migs.*, mid.database_id, mid.[object_id]
	FROM sys.dm_db_missing_index_groups mig
		INNER JOIN sys.dm_db_missing_index_group_stats migs ON migs.group_handle = mig.index_group_handle
		INNER JOIN sys.dm_db_missing_index_details mid ON mig.index_handle = mid.index_handle
	WHERE migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) * (migs.user_seeks + migs.user_scans) &gt; 10
	ORDER BY migs.avg_total_user_cost * migs.avg_user_impact * (migs.user_seeks + migs.user_scans) DESC