Introduzione
È spesso il caso in cui una transazione di MS SQL Server viene dimenticata dall'iniziatore. L'esempio migliore potrebbe essere il seguente:in SSMS viene eseguito uno script che, tramite l'istruzione "begin tran", avvia una transazione e si verifica un errore; tuttavia, "commit" o "rollback" non vengono eseguiti e l'iniziatore dell'esecuzione ha lasciato questa query per molto tempo. Di conseguenza, si verificano sempre più fluttuazioni quando si tratta di bloccare le query che richiedono l'accesso a risorse chiuse (tabelle e risorse del server come RAM, CPU e sistema di input-output).
In questo articolo, esamineremo uno dei modi in cui puoi automatizzare il processo di eliminazione delle transazioni dimenticate.
La soluzione
Definiamo una transazione dimenticata come una transazione attiva (attualmente eseguita) che, durante un intervallo di tempo sufficientemente ampio T, non ha query attive (attualmente eseguite).
Ecco l'algoritmo generale per eliminare tali transazioni:
- Creazione di una tabella per memorizzare e analizzare le informazioni sulle transazioni attualmente dimenticate, nonché una tabella per ordinare e archiviare le transazioni selezionate dalla prima tabella mediante azioni di eliminazione.
- Raccolta di informazioni (transazioni e relative sessioni che non hanno domande, ovvero le transazioni che sono state eseguite e dimenticate entro un intervallo di tempo specificato T.
- Aggiornamento della tabella contenente tutte le transazioni attualmente dimenticate che abbiamo ottenuto nel passaggio 1 (se una transazione dimenticata ha acquisito una query attiva, tale transazione verrà eliminata da questa tabella).
- Recupero delle sessioni che dobbiamo terminare (una sessione ha almeno una transazione che è stata inserita come dimenticata nella tabella dal passaggio 1.000 o più volte e la sessione aveva una query attiva mancante lo stesso numero di volte).
- Archiviazione dei dati che cancelleremo (dettagli sulle sessioni, connessioni e transazioni che verranno eliminate).
- Eliminazione delle sessioni selezionate.
- Eliminazione delle voci elaborate insieme a quelle che non possono essere rimosse e che sono rimaste nella tabella dal passaggio 1 per troppo tempo.
Ora, vediamo come implementare questo algoritmo.
Prima di tutto, dovremo creare una tabella per memorizzare e analizzare le informazioni su tutte le transazioni attualmente dimenticate:
USE [DB_NAME] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [srv].[SessionTran]( [SessionID] [int] NOT NULL, [TransactionID] [bigint] NOT NULL, [CountTranNotRequest] [tinyint] NOT NULL, [CountSessionNotRequest] [tinyint] NOT NULL, [TransactionBeginTime] [datetime] NOT NULL, [InsertUTCDate] [datetime] NOT NULL, [UpdateUTCDate] [datetime] NOT NULL, CONSTRAINT [PK_SessionTran] PRIMARY KEY CLUSTERED ( [SessionID] ASC, [TransactionID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO ALTER TABLE [srv].[SessionTran] ADD CONSTRAINT [DF_SessionTran_Count] DEFAULT ((0)) FOR [CountTranNotRequest] GO ALTER TABLE [srv].[SessionTran] ADD CONSTRAINT [DF_SessionTran_CountSessionNotRequest] DEFAULT ((0)) FOR [CountSessionNotRequest] GO ALTER TABLE [srv].[SessionTran] ADD CONSTRAINT [DF_SessionTran_InsertUTCDate] DEFAULT (getutcdate()) FOR [InsertUTCDate] GO ALTER TABLE [srv].[SessionTran] ADD CONSTRAINT [DF_SessionTran_UpdateUTCDate] DEFAULT (getutcdate()) FOR [UpdateUTCDate] GO
Qui:
1) SessionID — identificatore di sessione
2) TransactionID — identificatore di transazione dimenticata
3) CountTranNotRequest — il numero di volte in cui una transazione è stata registrata come dimenticata
4) CountSessionNotRequest — il numero di volte in cui una sessione senza query attive è stata registrata e si è verificata una transazione dimenticata
5) TransactionBeginTime — data e ora di avvio della transazione dimenticata
6) InsertUTCDate — data e ora di creazione della voce (UTC)
7) UpdateUTCDate:data e ora di aggiornamento della voce (UTC)
Successivamente, creeremo una tabella per archiviare e ordinare le transazioni dalla prima tabella mediante azioni di eliminazione:
[expand title =”Codice “]
USE [DB_NAME] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [srv].[KillSession]( [ID] [int] IDENTITY(1,1) NOT NULL, [session_id] [smallint] NOT NULL, [transaction_id] [bigint] NOT NULL, [login_time] [datetime] NOT NULL, [host_name] [nvarchar](128) NULL, [program_name] [nvarchar](128) NULL, [host_process_id] [int] NULL, [client_version] [int] NULL, [client_interface_name] [nvarchar](32) NULL, [security_id] [varbinary](85) NOT NULL, [login_name] [nvarchar](128) NOT NULL, [nt_domain] [nvarchar](128) NULL, [nt_user_name] [nvarchar](128) NULL, [status] [nvarchar](30) NOT NULL, [context_info] [varbinary](128) NULL, [cpu_time] [int] NOT NULL, [memory_usage] [int] NOT NULL, [total_scheduled_time] [int] NOT NULL, [total_elapsed_time] [int] NOT NULL, [endpoint_id] [int] NOT NULL, [last_request_start_time] [datetime] NOT NULL, [last_request_end_time] [datetime] NULL, [reads] [bigint] NOT NULL, [writes] [bigint] NOT NULL, [logical_reads] [bigint] NOT NULL, [is_user_process] [bit] NOT NULL, [text_size] [int] NOT NULL, [language] [nvarchar](128) NULL, [date_format] [nvarchar](3) NULL, [date_first] [smallint] NOT NULL, [quoted_identifier] [bit] NOT NULL, [arithabort] [bit] NOT NULL, [ansi_null_dflt_on] [bit] NOT NULL, [ansi_defaults] [bit] NOT NULL, [ansi_warnings] [bit] NOT NULL, [ansi_padding] [bit] NOT NULL, [ansi_nulls] [bit] NOT NULL, [concat_null_yields_null] [bit] NOT NULL, [transaction_isolation_level] [smallint] NOT NULL, [lock_timeout] [int] NOT NULL, [deadlock_priority] [int] NOT NULL, [row_count] [bigint] NOT NULL, [prev_error] [int] NOT NULL, [original_security_id] [varbinary](85) NOT NULL, [original_login_name] [nvarchar](128) NOT NULL, [last_successful_logon] [datetime] NULL, [last_unsuccessful_logon] [datetime] NULL, [unsuccessful_logons] [bigint] NULL, [group_id] [int] NOT NULL, [database_id] [smallint] NOT NULL, [authenticating_database_id] [int] NULL, [open_transaction_count] [int] NOT NULL, [most_recent_session_id] [int] NULL, [connect_time] [datetime] NULL, [net_transport] [nvarchar](40) NULL, [protocol_type] [nvarchar](40) NULL, [protocol_version] [int] NULL, [encrypt_option] [nvarchar](40) NULL, [auth_scheme] [nvarchar](40) NULL, [node_affinity] [smallint] NULL, [num_reads] [int] NULL, [num_writes] [int] NULL, [last_read] [datetime] NULL, [last_write] [datetime] NULL, [net_packet_size] [int] NULL, [client_net_address] [nvarchar](48) NULL, [client_tcp_port] [int] NULL, [local_net_address] [nvarchar](48) NULL, [local_tcp_port] [int] NULL, [connection_id] [uniqueidentifier] NULL, [parent_connection_id] [uniqueidentifier] NULL, [most_recent_sql_handle] [varbinary](64) NULL, [LastTSQL] [nvarchar](max) NULL, [transaction_begin_time] [datetime] NOT NULL, [CountTranNotRequest] [tinyint] NOT NULL, [CountSessionNotRequest] [tinyint] NOT NULL, [InsertUTCDate] [datetime] NOT NULL, CONSTRAINT [PK_KillSession] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO ALTER TABLE [srv].[KillSession] ADD CONSTRAINT [DF_KillSession_InsertUTCDate] DEFAULT (getutcdate()) FOR [InsertUTCDate] GO
[/espandi]
Qui, tutti i campi sono presi dalle rappresentazioni di sistema "sys.dm_exec_sessions" e "sys.dm_exec_connections" e "InsertUTCDate" specifica l'ora UTC in cui è stata creata la voce.
Quindi, per completare i passaggi rimanenti, implementiamo la procedura memorizzata [srv].[AutoKillSessionTranBegin] come segue:
[expand title =”Codice “]
USE [DB_NAME] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE [srv].[AutoKillSessionTranBegin] @minuteOld int, --age of the executed transaction (T min.) @countIsNotRequests int --amount of times it has been placed into the table (K) AS BEGIN SET NOCOUNT ON; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; declare @tbl table ( SessionID int, TransactionID bigint, IsSessionNotRequest bit, TransactionBeginTime datetime ); --retrieving information (transactions and theirs session which have no requests, i.e, transactions that were initiated and forgotten) insert into @tbl ( SessionID, TransactionID, IsSessionNotRequest, TransactionBeginTime ) select t.[session_id] as SessionID , t.[transaction_id] as TransactionID , case when exists(select top(1) 1 from sys.dm_exec_requests as r where r.[session_id]=t.[session_id]) then 0 else 1 end as IsSessionNotRequest , (select top(1) ta.[transaction_begin_time] from sys.dm_tran_active_transactions as ta where ta.[transaction_id]=t.[transaction_id]) as TransactionBeginTime from sys.dm_tran_session_transactions as t where t.[is_user_transaction]=1 and not exists(select top(1) 1 from sys.dm_exec_requests as r where r.[transaction_id]=t.[transaction_id]); --refreshing the table containing all initiated transactions with no requests ;merge srv.SessionTran as st using @tbl as t on st.[SessionID]=t.[SessionID] and st.[TransactionID]=t.[TransactionID] when matched then update set [UpdateUTCDate] = getUTCDate() , [CountTranNotRequest] = st.[CountTranNotRequest]+1 , [CountSessionNotRequest] = case when (t.[IsSessionNotRequest]=1) then (st.[CountSessionNotRequest]+1) else 0 end , [TransactionBeginTime] = t.[TransactionBeginTime] when not matched by target then insert ( [SessionID] ,[TransactionID] ,[TransactionBeginTime] ) values ( t.[SessionID] ,t.[TransactionID] ,t.[TransactionBeginTime] ) when not matched by source then delete; --list of sessions which need to be deleted (those that contain forgotten transactions) declare @kills table ( SessionID int ); --детальная информация для архива declare @kills_copy table ( SessionID int, TransactionID bigint, CountTranNotRequest tinyint, CountSessionNotRequest tinyint, TransactionBeginTime datetime ) --gathering the sessions we need to kill --a session has at least one transaction which was marked as having no requests @countIsNotRequests times --and this session was marked as having no active requests the same amount of times insert into @kills_copy ( SessionID, TransactionID, CountTranNotRequest, CountSessionNotRequest, TransactionBeginTime ) select SessionID, TransactionID, CountTranNotRequest, CountSessionNotRequest, TransactionBeginTime from srv.SessionTran where [CountTranNotRequest]>[email protected] and [CountSessionNotRequest]>[email protected] and [TransactionBeginTime]<=DateAdd(minute,[email protected],GetDate()); --archiving the data we need to delete (details on the sessions to be deleted, connections and transactions) INSERT INTO [srv].[KillSession] ([session_id] ,[transaction_id] ,[login_time] ,[host_name] ,[program_name] ,[host_process_id] ,[client_version] ,[client_interface_name] ,[security_id] ,[login_name] ,[nt_domain] ,[nt_user_name] ,[status] ,[context_info] ,[cpu_time] ,[memory_usage] ,[total_scheduled_time] ,[total_elapsed_time] ,[endpoint_id] ,[last_request_start_time] ,[last_request_end_time] ,[reads] ,[writes] ,[logical_reads] ,[is_user_process] ,[text_size] ,[language] ,[date_format] ,[date_first] ,[quoted_identifier] ,[arithabort] ,[ansi_null_dflt_on] ,[ansi_defaults] ,[ansi_warnings] ,[ansi_padding] ,[ansi_nulls] ,[concat_null_yields_null] ,[transaction_isolation_level] ,[lock_timeout] ,[deadlock_priority] ,[row_count] ,[prev_error] ,[original_security_id] ,[original_login_name] ,[last_successful_logon] ,[last_unsuccessful_logon] ,[unsuccessful_logons] ,[group_id] ,[database_id] ,[authenticating_database_id] ,[open_transaction_count] ,[most_recent_session_id] ,[connect_time] ,[net_transport] ,[protocol_type] ,[protocol_version] ,[encrypt_option] ,[auth_scheme] ,[node_affinity] ,[num_reads] ,[num_writes] ,[last_read] ,[last_write] ,[net_packet_size] ,[client_net_address] ,[client_tcp_port] ,[local_net_address] ,[local_tcp_port] ,[connection_id] ,[parent_connection_id] ,[most_recent_sql_handle] ,[LastTSQL] ,[transaction_begin_time] ,[CountTranNotRequest] ,[CountSessionNotRequest]) select ES.[session_id] ,kc.[TransactionID] ,ES.[login_time] ,ES.[host_name] ,ES.[program_name] ,ES.[host_process_id] ,ES.[client_version] ,ES.[client_interface_name] ,ES.[security_id] ,ES.[login_name] ,ES.[nt_domain] ,ES.[nt_user_name] ,ES.[status] ,ES.[context_info] ,ES.[cpu_time] ,ES.[memory_usage] ,ES.[total_scheduled_time] ,ES.[total_elapsed_time] ,ES.[endpoint_id] ,ES.[last_request_start_time] ,ES.[last_request_end_time] ,ES.[reads] ,ES.[writes] ,ES.[logical_reads] ,ES.[is_user_process] ,ES.[text_size] ,ES.[language] ,ES.[date_format] ,ES.[date_first] ,ES.[quoted_identifier] ,ES.[arithabort] ,ES.[ansi_null_dflt_on] ,ES.[ansi_defaults] ,ES.[ansi_warnings] ,ES.[ansi_padding] ,ES.[ansi_nulls] ,ES.[concat_null_yields_null] ,ES.[transaction_isolation_level] ,ES.[lock_timeout] ,ES.[deadlock_priority] ,ES.[row_count] ,ES.[prev_error] ,ES.[original_security_id] ,ES.[original_login_name] ,ES.[last_successful_logon] ,ES.[last_unsuccessful_logon] ,ES.[unsuccessful_logons] ,ES.[group_id] ,ES.[database_id] ,ES.[authenticating_database_id] ,ES.[open_transaction_count] ,EC.[most_recent_session_id] ,EC.[connect_time] ,EC.[net_transport] ,EC.[protocol_type] ,EC.[protocol_version] ,EC.[encrypt_option] ,EC.[auth_scheme] ,EC.[node_affinity] ,EC.[num_reads] ,EC.[num_writes] ,EC.[last_read] ,EC.[last_write] ,EC.[net_packet_size] ,EC.[client_net_address] ,EC.[client_tcp_port] ,EC.[local_net_address] ,EC.[local_tcp_port] ,EC.[connection_id] ,EC.[parent_connection_id] ,EC.[most_recent_sql_handle] ,(select top(1) text from sys.dm_exec_sql_text(EC.[most_recent_sql_handle])) as [LastTSQL] ,kc.[TransactionBeginTime] ,kc.[CountTranNotRequest] ,kc.[CountSessionNotRequest] from @kills_copy as kc inner join sys.dm_exec_sessions ES with(readuncommitted) on kc.[SessionID]=ES.[session_id] inner join sys.dm_exec_connections EC with(readuncommitted) on EC.session_id = ES.session_id; --gathering sessions insert into @kills ( SessionID ) select [SessionID] from @kills_copy group by [SessionID]; declare @SessionID int; --deleting sessions while(exists(select top(1) 1 from @kills)) begin select top(1) @SessionID=[SessionID] from @kills; BEGIN TRY EXEC sp_executesql N'kill @SessionID', N'@SessionID INT', @SessionID; END TRY BEGIN CATCH END CATCH delete from @kills where [SessionID][email protected]; end select st.[SessionID] ,st.[TransactionID] into #tbl from srv.SessionTran as st where st.[CountTranNotRequest]>=250 or st.[CountSessionNotRequest]>=250 or exists(select top(1) 1 from @kills_copy kc where kc.[SessionID]=st.[SessionID]); --Deleting the processed entries along with those that cannot be removed and have been in the table for too long delete from st from #tbl as t inner join srv.SessionTran as st on t.[SessionID] =st.[SessionID] and t.[TransactionID]=st.[TransactionID]; drop table #tbl; END GO
[/espandi]
Il passaggio 7 dell'algoritmo viene implementato attraverso uno di questi due contatori – CountTranNotRequest o CountSessionNotRequest – raggiungendo un valore di 250.
Il risultato
In questo articolo abbiamo esaminato l'implementazione di un processo che elimina automaticamente le transazioni dimenticate.
Questo metodo ci consente di automatizzare il processo di eliminazione delle transazioni dimenticate. Ciò si traduce nella diminuzione o nell'arresto della crescita della fluttuazione nel blocco prodotto da tali transazioni. Pertanto, le prestazioni del DBMS sono protette dalle azioni che possono comportare transazioni dimenticate.
Fonti:
» sys.dm_exec_requests
» sys.dm_tran_active_transactions
» sys.dm_tran_session_transactions
» sys.dm_exec_sql_text
» sys.dm_exec_sessions
» sys.dm_exec_connections
» KILL