/* * This script will identify which transactions are causing excessive growth of the version store. * It will output a list of how much version store retention each transaction is responsible * This retention is not "exclusive" to that single transaction and overlaps with others * if there are multiple long-lived transactions. */ --Meaningless magic numbers. Replace with thresholds meaningful to your system! DECLARE @version_store_size_total_threshold BIGINT = 25 * POWER(2.0, 30); --GB DECLARE @version_store_size_per_db_threshold BIGINT = 10 * POWER(2.0, 30); --GB ------------------------------------------------------------- DECLARE @version_store_reserved_size_exceeded BIT = 0; DECLARE @total_version_store_size BIGINT = 0; DECLARE @largest_db_version_store_size BIGINT = 0; ------------------------------------------------------------- --Check if the version store reserved size exceeds our threshold --If it does not, the in-use size cannot possibly exceed them, so bail early IF OBJECT_ID('sys.dm_tran_version_store_space_usage') IS NOT NULL BEGIN; --Utilize the summary DMV introduced in SQL2017 / SQL2016SP2 / SQL2014SP3 SELECT @total_version_store_size = SUM(reserved_space_kb) * 1024 , @largest_db_version_store_size = MAX(reserved_space_kb) * 1024 FROM sys.dm_tran_version_store_space_usage ; IF @total_version_store_size > @version_store_size_total_threshold OR @largest_db_version_store_size > @version_store_size_per_db_threshold SET @version_store_reserved_size_exceeded = 1; END; ELSE BEGIN; --If the DMV is not available, use the system-wide perfmon counter. SET @total_version_store_size = ( SELECT pc.cntr_value * 1024 FROM sys.dm_os_performance_counters pc WHERE pc.counter_name = 'Version Store Size (KB)' ); --The perfmon counter does not allow per-db reserved size identification --So we pessimistically assume one DB is responsible for all of it --The detailed analysis later in the script will still filter appropriately IF @total_version_store_size > @version_store_size_total_threshold OR @total_version_store_size > @version_store_size_per_db_threshold SET @version_store_reserved_size_exceeded = 1; END; ------------------------------------------------------------- IF (@version_store_reserved_size_exceeded = 1) BEGIN; CREATE TABLE #version_store_tail_size ( database_id SMALLINT , transaction_sequence_num BIGINT , sequence_num_discrete_size_bytes BIGINT , sequence_num_tail_size_bytes BIGINT INDEX ix_version_store_tail_size_tail NONCLUSTERED , recoverable_space_global BIGINT , recoverable_space_per_db BIGINT , CONSTRAINT PK_version_store_tail_size PRIMARY KEY CLUSTERED (database_id, transaction_sequence_num) ); CREATE TABLE #database_sequence_num_watermark ( database_id SMALLINT INDEX ix_database_sequence_num_watermark_id CLUSTERED , transaction_sequence_num BIGINT INDEX ix_database_sequence_num_watermark_tsn NONCLUSTERED ); /* * Examining the detailed values in the version store can take a significant amount of time * The processing speed is in the neighborhood of 5-15 GB/min */ ;WITH version_store_sequence_sums AS ( SELECT tvs.database_id , tvs.transaction_sequence_num , SUM(CONVERT(BIGINT, tvs.record_length_first_part_in_bytes + tvs.record_length_second_part_in_bytes)) AS [sequence_size_bytes] FROM sys.dm_tran_version_store tvs GROUP BY tvs.database_id, tvs.transaction_sequence_num ), version_store_tail_size AS ( SELECT vsss.database_id , vsss.transaction_sequence_num , vsss.sequence_size_bytes , SUM(vsss.sequence_size_bytes) OVER (PARTITION BY vsss.database_id ORDER BY vsss.transaction_sequence_num DESC RANGE UNBOUNDED PRECEDING) AS [tail_size_bytes] FROM version_store_sequence_sums vsss ), version_store_recoverable_space AS ( SELECT vsts.database_id , vsts.transaction_sequence_num , vsts.sequence_size_bytes , vsts.tail_size_bytes , SUM(vsts.sequence_size_bytes) OVER (ORDER BY vsts.tail_size_bytes DESC RANGE UNBOUNDED PRECEDING) recoverable_space_global , SUM(vsts.sequence_size_bytes) OVER (PARTITION BY vsts.database_id ORDER BY vsts.tail_size_bytes DESC RANGE UNBOUNDED PRECEDING) recoverable_space_per_db FROM version_store_tail_size vsts ) INSERT #version_store_tail_size (database_id, transaction_sequence_num, sequence_num_discrete_size_bytes, sequence_num_tail_size_bytes, recoverable_space_global, recoverable_space_per_db) SELECT vsrs.database_id, vsrs.transaction_sequence_num, vsrs.sequence_size_bytes, vsrs.tail_size_bytes, vsrs.recoverable_space_global, vsrs.recoverable_space_per_db FROM version_store_recoverable_space vsrs ; INSERT #database_sequence_num_watermark (database_id, transaction_sequence_num) SELECT vsts.database_id , MAX(vsts.transaction_sequence_num) FROM #version_store_tail_size vsts WHERE vsts.recoverable_space_global > @version_store_size_total_threshold OR vsts.recoverable_space_per_db > @version_store_size_per_db_threshold GROUP BY vsts.database_id ; --Final output SELECT SYSDATETIME() AS [runtime] , s.[last_request_start_time] AS [last_batch] , s.[host_name] , s.[login_name] , s.[program_name] , tasdt.session_id , tasdt.transaction_sequence_num , tdt.database_id --, vsts.recoverable_space_global AS [version_store_recoverable_space_bytes] , CONVERT(NUMERIC(8,3), vsts.recoverable_space_global / (POWER(2.0, 30))) AS [version_store_recoverable_space_gb] , CASE ROW_NUMBER() OVER (PARTITION BY tasdt.session_id ORDER BY tdt.database_id) WHEN 1 THEN N'KILL ' + CONVERT(NVARCHAR(20), tasdt.session_id) + N';' ELSE '' END AS [kill_command] FROM [sys].[dm_tran_active_snapshot_database_transactions] [tasdt] INNER JOIN sys.dm_tran_database_transactions tdt ON tdt.transaction_id = tasdt.transaction_id INNER JOIN #database_sequence_num_watermark dsnw ON dsnw.database_id = tdt.database_id AND ( dsnw.transaction_sequence_num >= tasdt.first_snapshot_sequence_num OR dsnw.transaction_sequence_num >= tasdt.transaction_sequence_num ) INNER JOIN #version_store_tail_size vsts ON vsts.database_id = dsnw.database_id AND vsts.transaction_sequence_num = dsnw.transaction_sequence_num INNER JOIN sys.dm_exec_sessions s ON s.session_id = tasdt.session_id ; DROP TABLE #database_sequence_num_watermark; DROP TABLE #version_store_tail_size; END;