Je sais ce que vous allez dire: on l’a déjà vu partout, il y a déjà un million d’articles sur le sujet, quel intérêt pour une société qui se défend justement de poster sur des sujets originaux, etc…
Je vous répondrai que le problème n’est pas trivial. Tout le monde utilise les vues DMV pour récupérer les infos de la session qui occupe l’espace dans tempdb, notamment le texte SQL de la requête:
– sys.dm_db_task_space_usage: pour voir l’espace utilisé par session.
– sys.dm_exec_requests ou sys.dm_exec_query_stats + cross apply sys.dm_exec_sql_text(sql_handle): pour plus d’infos sur les sessions (stats + texte des requêtes).
Seulement voilà: les sessions dont le plan a été purgé du cache ne sont plus visibles dans les DMV sys.dm_exec… donc la jointure entre sys.dm_db_task_space_usage et sys.dm_exec… ne renverra pas d’infos pour des sessions qui utilisent pourtant bel et bien de l’espace dans tempdb.
Donc il faut avoir recours au bon vieux dbcc inputbuffer pour récupérer tout ça.
1) D’abord, on va créer les tables de recueil.
1.1 DBCCINPUTBUFFER va nous permettre de récupérer le contenu du dbcc inputbuffer par session:
CREATE TABLE tempdb.guest.DBCCINPUTBUFFER ( EventType varchar(50), Parameters int, SQLText varchar(8000) )
1.2 DBCCSHOWFILESTATS va nous permettre de récupérer l’espace total et utilisé dans tempdb:
CREATE TABLE tempdb.guest.DBCCSHOWFILESTATS ( [fileid] [int], [filegroup] [int], [totalextents] [int], [usedextents] [int], [name] [varchar] (255), [filename] [varchar] (255) )
1.3 monitorTempdb sera notre table finale d’historique.
CREATE TABLE tempdb.guest.monitorTempdb ( curdate datetime, spaceintempdb bigint, spaceusedintempdb bigint, spid int, sqltext varchar(8000), internal_objects_alloc_MB bigint, internal_objects_dealloc_MB bigint, user_objects_alloc_MB bigint, user_objects_dealloc_MB bigint, program_name varchar(100), login_time datetime, hostname varchar(20), login_name varchar(100) )
2) Par session récupérer les infos et le texte de la requête: on va placer les sessions consommatrices ainsi que les espaces consommés dans un curseur, puis boucler pour chaque session_id et récupérer plus d’infos (program_name, login, etc… depuis sys.dm_exec_sessions, la seule DMV qui affiche toutes les sessions actives ou non) et le dernier texte SQL généré:
use tempdb go declare @spid int, @sqltext varchar(8000), @internal_objects_alloc_page_count bigint, @internal_objects_dealloc_page_count bigint, @user_objects_alloc_page_count bigint, @user_objects_dealloc_page_count bigint, @program_name varchar(100), @login_time datetime, @hostname varchar(20), @login_name varchar(100), @SQLQUERY varchar(100), @spaceintempdb bigint, @spaceusedintempdb bigint declare cr_sqltext CURSOR READ_ONLY for select session_id,internal_objects_alloc_page_count,internal_objects_dealloc_page_count, user_objects_alloc_page_count, user_objects_dealloc_page_count from sys.dm_db_task_space_usage where internal_objects_alloc_page_count > 12000 open cr_sqltext fetch next from cr_sqltext into @spid, @internal_objects_alloc_page_count, @internal_objects_dealloc_page_count, @user_objects_alloc_page_count, @user_objects_dealloc_page_count while @@fetch_status = 0 begin select @program_name = program_name, @login_time = login_time, @hostname = host_name, @login_name = login_name from sys.dm_exec_sessions where session_id = @spid select @SQLQUERY = 'dbcc inputbuffer('+convert(char(3),@spid)+')' insert into tempdb.guest.DBCCINPUTBUFFER exec (@SQLQUERY) select @sqltext = SQLText from tempdb.guest.DBCCINPUTBUFFER insert into tempdb.guest.DBCCSHOWFILESTATS exec ('DBCC showfilestats with NO_INFOMSGS') select @spaceintempdb = sum(totalextents)/16, @spaceusedintempdb = sum(usedextents)/16 from tempdb.guest.DBCCSHOWFILESTATS delete from tempdb.guest.DBCCINPUTBUFFER delete from tempdb.guest.DBCCSHOWFILESTATS insert tempdb.guest.monitorTempdb values (getdate(), @spaceintempdb, @spaceusedintempdb , @spid, @sqltext, @internal_objects_alloc_page_count*8192/1048576 , @internal_objects_dealloc_page_count*8192/1048576 , @user_objects_alloc_page_count*8192/1048576 , @user_objects_dealloc_page_count*8192/1048576 , @program_name , @login_time , @hostname , @login_name) fetch next from cr_sqltext into @spid, @internal_objects_alloc_page_count, @internal_objects_dealloc_page_count, @user_objects_alloc_page_count, @user_objects_dealloc_page_count end close cr_sqltext deallocate cr_sqltext go
Cette dernière partie peut être mise dans un job planifié toutes les minutes par exemple. C’est ainsi que j’ai découvert que sp_MSget_repl_commands et sp_MSdistribution_cleanup (procs de répli) peuvent être extrêmement consommatrices lorsque la file des requêtes à répliquer est très importante.
A+ [ David B. ]
Continuez votre lecture sur le blog :
- DMV et problème de tri (David Baffaleuf) [SQL Server]
- How-To : réduire l’enveloppe de tempdb (David Baffaleuf) [SQL Server]
- Intérêt de créer des indexes cluster uniques (David Baffaleuf) [SQL Server]
- Alter table rebuild (Benjamin VESAN) [SQL Server]
- Récupérer l’espace consommé par le versionning de lignes (David Baffaleuf) [SQL Server]
super ce petit bout de code !!!! Petite remarque : je place mes tables hors tempdb. Pour les qq fois utilisées dans mon cas, la tempdb se remplissait extrêmement vite, rollback du traitement impossbible, SQL Server s’arrête tout seul…. perte des tables tempo évidemment 🙂