Въведение
Често се случва, когато транзакция на MS SQL Server е забравена от инициатора. Най-добрият пример би бил следният:скрипт се изпълнява в SSMS, който чрез инструкцията „begin tran“ стартира транзакция и възниква грешка; обаче „commit“ или „rollback“ не преминават и инициаторът на изпълнение е оставил тази заявка за дълго време. В резултат на това се появяват все повече и повече флуктуации, когато става въпрос за блокиране на заявките, които изискват достъп до затворени ресурси (таблици и сървърни ресурси като RAM, CPU и входно-изходната система).
В тази статия ще разгледаме един от начините, по които можете да автоматизирате процеса на изтриване на забравена транзакция.
Решението
Нека дефинираме забравена транзакция като активна (изпълнена в момента) транзакция, която по време на достатъчно голям период от време T няма активни (изпълнени в момента) заявки.
Ето общия алгоритъм за изтриване на такива транзакции:
- Създаване на таблица за съхраняване и анализиране на информация за текущо забравени транзакции, както и таблица за сортиране и архивиране на транзакциите, избрани от първата таблица чрез действия за изтриване.
- Събиране на информация (транзакции и техните сесии, които нямат заявки, т.е. транзакциите, които са били изпълнени и забравени в рамките на определен период от време T.
- Опресняване на таблицата, съдържаща всички в момента забравени транзакции, които получихме в стъпка 1 (ако забравена транзакция е получила активна заявка, тогава такава транзакция ще бъде изтрита от тази таблица).
- Извличане на сесиите, които трябва да прекратим (в една сесия има поне една транзакция, която е била поставена като забравена в таблицата от стъпка 1 K или повече пъти и сесията е имала липсваща активна заявка същия брой пъти).
- Архивиране на данните, които ще изтрием (подробности за сесиите, връзките и транзакциите, които ще бъдат убити).
- Изтриване на избраните сесии.
- Изтриване на обработените записи заедно с тези, които не могат да бъдат премахнати и са били в таблицата от стъпка 1 твърде дълго.
Сега нека видим как можем да внедрим този алгоритъм.
Първо, ще трябва да създадем таблица, която да съхранява и анализира информацията за всички забравени в момента транзакции:
ИЗПОЛЗВАЙТЕ [DB_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SessionTran]( [SessionID] [int] NOT NULL, [TransactionID] [bigint] NOT NULL, [CountTrannyintRequest]NOTSCounter ] [tinyint] NOT NULL, [TransactionBeginTime] [datetime] NOT NULL, [InsertUTCDate] [datetime] NOT NULL, [UpdateUTCDate] [datetime] NOT NULL, CONSTRAINT [PK_SessionTran] PRIMARY KEY CLUSTERED, [ASSCesTransID] ASC)С (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_PAGE_LOCKS =ON) ВКЛ. 0)) ЗА [CountTranNotRequest]GOALTER TABLE [srv].[SessionTran] ДОБАВЯНЕ НА ОГРАНИЧЕНИЕ [DF_SessionTran_CountSessionNotRequest] ПО ПОДРАЗБИРАНЕ ((0)) ЗА [CountSessionNotRequest]GOALTER TABLE [srv].[SessionTran] ADD CONSTRAN (SessionTran) [SessionTran] ADD_CONSTRAN (SessionTran) [SessionTran] ADD CONSSESSION (TDF_Tran) ) ЗА [InsertUTCDate]GOALTER TABLE [srv].[Session Tran] ДОБАВЯНЕ НА ОГРАНИЧЕНИЕ [DF_SessionTran_UpdateUTCDate] ПО ПОДРАЗБИРАНЕ (getutcdate()) ЗА [UpdateUTCDate]GO
Тук:
1) SessionID — идентификатор на сесия
2) TransactionID — идентификатор на забравена транзакция
3) CountTranNotRequest — колко пъти една транзакция е била регистрирана като забравена
4) CountSessionNotRequest — количеството пъти на сесия без активни заявки е регистриран и има забравена транзакция
5) TransactionBeginTime — дата и час на иницииране на забравената транзакция
6) InsertUTCDate — дата и час на създаване на запис (UTC)
7) UpdateUTCDate — дата и час на актуализация (UTC)
След това ще създадем таблица за архивиране и сортиране на транзакциите от първата таблица чрез действия за изтриване:
[expand title =”Код “]
ИЗПОЛЗВАЙТЕ [DB_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[KillSession]( [ID] [int] IDENTITY(1,1) НЕ NULL, [session_id] [smallint] НЕ NULL, [transaction_id] [transaction_id] ] НЕ NULL, [време_за_вход] [дата и час] NOT NULL, [име_хост] [nvarchar](128) NULL, [име_на_програма] [nvarchar](128) NULL, [идентификатор на_хост_процес] [int] NULL, [версия_клиент] [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) НЕ NULL, [context_info] [varbinary](128) NULL, [cpu_time] [int] НЕ NULL, [memory_usage] [int] НЕ NULL, [ total_scheduled_time] [int] НЕ NULL, [total_elapsed_time] [int] НЕ NULL, [endpoint_id] [int] НЕ NULL, [last_request_start_time] [datetime] NOT NULL, [last_request_end_time] [datetime] [OTbigin] NOT, [treads] NULL, [записва] [bigint] НЕ NULL, [логически_четения] [bigint] НЕ 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] НЕ NULL, [arithabort] [bit] NOT NULL, [ansi_null_dflt_on] [bit] NOT NULL, [ansi_defaults] [bit] НЕ NULL, [ansi_warnings] [bit] NOT NULL, [ansi_padding] [bit] NOT NULL, [ansi_ [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] 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_NULL] [unsuccessful_logons] [bigint] NULL, [group_id] [int] НЕ NULL, [database_id] [smallint] НЕ NULL, [authenticating_database_id] [int] NULL, [open_transaction_count] [int] NOT_recent_ NULL, [msession_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, [afinity_node] [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] [unique_id, NULL [parent_connection_id] [уникален идентификатор] NULL, [most_recent_sql_handle] [varbinary](64) NULL, [LastTSQL] [nvarchar](max) NULL, [transaction_begin_time] [datetime] NOT NULL, [CountTrantinyesNuLL] ] [tinyint] НЕ NULL, [InsertUTCDate] [datetime] NOT NULL, CONSTRAINT [PK_KillSession] ПЪРВИЧЕН КЛУСТРИРОВАН ( [ID] ASC) С (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEYCOW ON, ALLOW_PAGE_LOCKS =ON) ON [PRIMARY]) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]GOALTER TABLE [srv].[KillSession] ДОБАВЯНЕ НА ОГРАНИЧЕНИЕ [DF_KillSession_InsertUTCDate] ПО ПОДРАЗБИРАНЕ (getutcdate [GOALTER())Da ][/expand]
Тук всички полета са взети от системните представяния „sys.dm_exec_sessions“ и „sys.dm_exec_connections“, а „InsertUTCDate“ указва UTC часа, в който записът е създаден.
След това, за да завършим останалите стъпки, нека внедрим съхранената процедура [srv].[AutoKillSessionTranBegin], както следва:
[expand title =”Код “]
ИЗПОЛЗВАЙТЕ [DB_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[AutoKillSessionTranBegin] @minuteOld int, --възраст на изпълнената транзакция (T min.) @countIsNotRequests колко пъти е била поставена в int --количеството пъти е била поставена int. таблица (K)ASBEGIN SET NOCOUNT ON; ЗАДАВАНЕ НА НИВОТО НА ИЗОЛАЦИЯ НА ТРАНЗАКЦИЯТА ПРОЧЕТЕНЕ НЕЗАДЪЛЖЕНО; декларира таблица @tbl ( SessionID int, TransactionID bigint, IsSessionNotRequest bit, TransactionBeginTime datetime ); --извличане на информация (транзакциите и тяхната сесия, които нямат заявки, т.е. транзакции, които са били инициирани и забравени) вмъкнете в @tbl (SessionID, TransactionID, IsSessionNotRequest, TransactionBeginTime) изберете t.[session_id] като SessionID, t.[transaction_id] като TransactionID , случай, когато съществува (изберете top(1) 1 от sys.dm_exec_requests като r, където r.[session_id]=t.[session_id]), след това 0 else 1 завършва като IsSessionNotRequest , (изберете top(1) ta.[transaction_begin_time]). ] от sys.dm_tran_active_transactions като ta, където ta.[transaction_id]=t.[transaction_id]) като TransactionBeginTime от sys.dm_tran_session_transactions като t, където t.[is_user_transaction]=1 и не съществува (изберете top(1) request 1 от sys. като r, където r.[transaction_id]=t.[transaction_id]); --опресняване на таблицата, съдържаща всички инициирани транзакции без заявки;сливане srv.SessionTran като st, като се използва @tbl като t на st.[SessionID]=t.[SessionID] и st.[TransactionID]=t.[TransactionID] при съвпадение. след това набор за актуализиране [UpdateUTCDate] =getUTCDate() , [CountTranNotRequest] =st.[CountTranNotRequest]+1 , [CountSessionNotRequest] =случай, когато (t.[IsSessionNotRequest]=1), след това (st.[CountSessionNotRequest]+1) друго 0 0. end , [TransactionBeginTime] =t.[TransactionBeginTime], когато не съответства на целта, след това вмъкнете ( [SessionID] ,[TransactionID] ,[TransactionBeginTime] ) стойности ( t.[SessionID] ,t.[TransactionID] ,t.[TransactionBeginTime] ) когато не съответства на източника, изтрийте; --списък на сесиите, които трябва да бъдат изтрити (тези, които съдържат забравени транзакции) декларира @kills таблица ( SessionID int ); --подробна информация за архива declare @kills_copy таблица ( SessionID int, TransactionID bigint, CountTranNotRequest tinyint, CountSessionNotRequest tinyint, TransactionBeginTime datetime ) --събиране на сесиите, които трябва да убием --сесия има поне една транзакция, която е маркирана като нямаща искания @countIsNotRequests пъти -- и тази сесия беше маркирана като без активни заявки, същия брой пъти, вмъкнати в @kills_copy ( SessionID, TransactionID, CountTranNotRequest, CountSessionNotRequest, TransactionBeginTime ) изберете SessionID, TransactionID, CountTranSquestS, TransactionTime от sccountTranNotRequest. където [CountTranNotRequest]>[email protected] и [CountSessionNotRequest]>[email protected] и [TransactionBeginTime]<=DateAdd(minute,[email protected],GetDate()); --архивиране на данните, които трябва да изтрием (подробности за сесиите, които трябва да бъдат изтрити, връзки и транзакции) 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,du_led],[cpu_time,du_led] [total_elapsed_time], [endpoint_id], [last_request_start_time], [last_request_end_time], [Reads], [Writes], [logical_reads], [is_user_process], [text_size], [език], [date_format], [date_first], [quoted_identier ] ,[arithabort] ,[ansi_null_dflt_on] ,[ansi_defaults] ,[ansi_warnings] ,[ansi_padding] ,[ansi_nulls] ,[concat_null_yields_null] ,[transaction_isolation_level] ,[lock_timeout] ,[deadlock_priority], [deadlock_priority], [название_на_запис],[наименование_на_в_секретен_идентификатор],[наименование_на_в_секретарии] 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] ,[net_packet_size] ,[client_net_address] ,[client_tcp_n] ,[local_n] et_address] ,[local_tcp_port] ,[connection_id] ,[parent_connection_id] ,[most_recent_sql_handle] ,[LastTSQL] ,[transaction_begin_time] ,[CountTranNotRequest] ,[CountSessionNotRequest] ,[CountSessionNotRequest] ,[CountSessionNotRequest] ,[CountSessionNotRequest] ,[CountSessionNotRequest],[CountSessionNotRequest],[CountSessionNotRequest],[CountSessionNotRequest].[SessionNotRequest]. [време_за_вход] ,ES.[име_на_хост] ,ES.[име_на_програма] ,ES.[идентификатор_на_процеса] ,ES.[версия_на_клиент] ,ES.[име_на_интерфейс_на_клиент] ,ES.[идентификатор_на_сигурност] ,ES.[име_за_вход] ,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.[чете] ,ES.[записва] ,ES.[логически_четения] ,ES.[is_user_process] ] ,ES.[текст_размер] ,ES.[език] ,ES.[формат_дата] ,ES.[първа_дата] ,ES.[цитат_идентификатор] ,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_uccessful_logon] ,ES.[last_unsuccessful_logon] ,ES.[unsuccessful_logons] ,ES.[group_id] ,ES.[database_id] ,ES]. ] ,ES.[open_transaction_count] ,EC.[most_recent_session_id] ,EC.[connect_time] ,EC.[net_transport] ,EC.[protocol_type] ,EC.[protocol_version] ] ,EC.[опция_шифроване] ,EC.[auth_scheme] ,EC.[афинитет на възел] ,EC.[брой_четения] ,EC.[брой_записи] ,EC.[последно_прочетено] ,EC.[последно_записване] ,EC.[размер_на_пакета] , 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] ,(изберете текст отгоре)(1) sys.dm_exec_sql_text(EC.[most_recent_sql_handle])) като [LastTSQL] ,kc.[TransactionBeginTime] ,kc.[CountTranNotRequest] ,kc.[CountSessionNotRequest] от @kills_copy като kc inner join sys(dsession km_exe с помощта на sys.d) .[SessionID]=ES.[session_id] вътрешно присъединяване sys.dm_exec_connections EC с(readuncommitted) на EC.session_id =ES.session_id; --събиране на сесии вмъкване в @kills (SessionID) изберете [SessionID] от групата @kills_copy от [SessionID]; декларира @SessionID int; --изтриване на сесии while(exists(select top(1) 1 from @kills)) start select top(1) @SessionID=[SessionID] от @kills; ЗАПОЧНЕТЕ ОПИТАЙТЕ EXEC sp_executesql N'kill @SessionID', N'@SessionID INT', @SessionID; END TRY BEGIN CATCH END CATCH изтриване от @kills където [SessionID][email protected]; край изберете st.[SessionID] ,st.[TransactionID] в #tbl от srv.SessionTran като st където st.[CountTranNotRequest]>=250 или st.[CountSessionNotRequest]>=250 или съществува (изберете top(1) 1 от от @kills_copy kc където kc.[SessionID]=st.[SessionID]); --Изтриване на обработените записи заедно с тези, които не могат да бъдат премахнати и са били в таблицата твърде дълго, изтрийте от st от #tbl as t inner join srv.SessionTran като st на t.[SessionID] =st.[SessionID] и t.[TransactionID]=st.[TransactionID]; пуснете таблица #tbl;ENDGO[/expand]
Стъпка 7 от алгоритъма се изпълнява чрез един от тези два брояча – CountTranNotRequest или CountSessionNotRequest – достигайки стойност от 250.
Резултатът
В тази статия разгледахме реализация на процес, който автоматично изтрива забравени транзакции.
Този метод ни позволява да автоматизираме процеса на изтриване на забравена транзакция. Това води до намаляване или спиране на нарастването на колебанията в блокирането, произведено от такива транзакции. Така производителността на СУБД е защитена от действия, които могат да доведат до забравени транзакции.
Източници:
» sys.dm_exec_requests
» sys.dm_tran_active_transactions
» sys.dm_tran_session_transactions
» sys.dm_exec_sql_text
» sys.dm_exec_sessions
» sys.