{"id":293,"date":"2010-06-05T09:33:02","date_gmt":"2010-06-05T12:33:02","guid":{"rendered":"http:\/\/fabriciodba.wordpress.com\/2010\/06\/05\/passo-a-passo-para-encontrar-as-querys-mais-demoradas-do-banco-de-dados-parte-1"},"modified":"2016-09-11T18:10:52","modified_gmt":"2016-09-11T21:10:52","slug":"passo-a-passo-para-encontrar-as-querys-mais-demoradas-do-banco-de-dados-parte-1","status":"publish","type":"post","link":"https:\/\/fabriciolima.net\/blog\/2010\/06\/05\/passo-a-passo-para-encontrar-as-querys-mais-demoradas-do-banco-de-dados-parte-1\/","title":{"rendered":"Passo a passo para encontrar as querys mais demoradas do Banco de Dados &#8211; Parte 1"},"content":{"rendered":"<div id=\"msgcns!874FDFFD5EC76B2E!441\" class=\"bvMsg\">\n<p>Fala Pessoal,<\/p>\n<p>Quem n\u00e3o quer saber quais s\u00e3o as <em>querys<\/em> mais demoradas do seu ambiente de Banco de Dados? Em quais hor\u00e1rios essas <em>querys<\/em> rodam? Quem est\u00e1 executando? De onde est\u00e1 sendo rodada? Quais os recursos que essas<em> querys<\/em> consomem?<\/p>\n<p>Nesse artigo, demonstrarei como eu fa\u00e7o esse acompanhamento no meu ambiente de trabalho. Ele \u00e9 v\u00e1lido para as vers\u00f5es 2005 e 2008 do SQL Server. Para a vers\u00e3o do SQL Server 2000, algumas colunas do<em> trace<\/em> devem ser alteradas pois n\u00e3o existem no SQL 2000.<\/p>\n<p>Primeiramente, deve ser criada uma tabela para armazenar o <em>log<\/em> das <em>querys<\/em> mais demoradas que rodam no nosso banco de dados. Deve-se escolher a <em>database<\/em> adequada do seu ambiente para armazenar essa tabela. Tamb\u00e9m criei um \u00edndice nessa tabela para efetuar as buscas pela data em que a<em> query<\/em> rodou.<\/p>\n<pre class=\"lang:tsql decode:true \">\r\nCREATE TABLE dbo.Traces (\r\n    [TextData] VARCHAR(MAX) NULL,\r\n    [NTUserName] VARCHAR(128) NULL,\r\n    [HostName] VARCHAR(128) NULL,\r\n    [ApplicationName] VARCHAR(128) NULL,\r\n    [LoginName] VARCHAR(128) NULL,\r\n    [SPID] INT NULL,\r\n    [Duration] NUMERIC(15, 2) NULL,\r\n    [StartTime] DATETIME NULL,\r\n    [EndTime] DATETIME NULL,\r\n    [Reads] INT,\r\n    [Writes] INT,\r\n    [CPU] INT,\r\n    [ServerName] VARCHAR(128) NULL,\r\n    [DataBaseName] VARCHAR(128),\r\n    [RowCounts] INT,\r\n    [SessionLoginName] VARCHAR(128)\r\n)\r\n\r\n-- Para realizar as querys de busca pela data que a query rodou.    \r\nCREATE CLUSTERED INDEX [SK01_Traces] ON [Traces]([StartTime]) WITH(FILLFACTOR = 95)\r\n<\/pre>\n<p>Em seguida, criaremos uma <em>procedure<\/em> para criar o arquivo de <em>trace<\/em> que ficar\u00e1 rodando em <em>backgroud<\/em> no servidor.<\/p>\n<p>Nessa <em>procedure<\/em> \u00e9 criado um<em> trace<\/em> com a procedure <em>sp_trace_create,<\/em> onde especificamos o caminho que esse <em>trace<\/em> ser\u00e1 armazenado. No meu caso criei uma pasta chamada <em>Trace<\/em> no <em>C:<\/em> do meu servidor. Voc\u00ea deve criar esse diret\u00f3rio antes de rodar o procedimento. Caso queria armazenar em um caminho diferente basta mudar o caminho na <em>procedure<\/em>.<\/p>\n<p>Com a procedure <em>sp_trace_setevent<\/em>, n\u00f3s definimos quais os eventos que nosso <em>trace<\/em> pegar\u00e1. Neste caso, armazenaremos os eventos:<\/p>\n<table border=\"0\" width=\"724\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td width=\"94\"><strong>Event Number<\/strong><\/td>\n<td width=\"159\"><strong>Event Name<\/strong><\/td>\n<td width=\"469\"><strong>Description<\/strong><\/td>\n<\/tr>\n<tr>\n<td width=\"94\" align=\"center\">10<\/td>\n<td width=\"159\">RPC:Completed<\/td>\n<td width=\"469\">Occurs when a remote procedure call (RPC) has completed.<\/td>\n<\/tr>\n<tr>\n<td width=\"94\" align=\"center\">12<\/td>\n<td width=\"159\">SQL:BatchCompleted<\/td>\n<td width=\"469\">Occurs when a Transact-SQL batch has completed.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Ap\u00f3s selecionarmos os eventos, devemos escolher as colunas que desejamos visualizar. As colunas que utilizo no meu ambiente s\u00e3o as seguintes:<\/p>\n<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td width=\"81\"><strong>Column Number<\/strong><\/td>\n<td width=\"172\"><strong>Column Name<\/strong><\/td>\n<td width=\"488\"><strong>Description<\/strong><\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">1<\/td>\n<td width=\"172\" align=\"center\">TextData<\/td>\n<td width=\"488\">Text value dependent on the event class that is captured in the trace.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">6<\/td>\n<td width=\"172\" align=\"center\">NTUserName<\/td>\n<td width=\"488\">Microsoft Windows user name.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">8<\/td>\n<td width=\"172\" align=\"center\">HostName<\/td>\n<td width=\"488\">Name of the client computer that originated the request.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">10<\/td>\n<td width=\"172\" align=\"center\">ApplicationName<\/td>\n<td width=\"488\">Name of the client application that created the connection to an instance of SQL Server. This column is populated with the values passed by the application rather than the displayed name of the program.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">11<\/td>\n<td width=\"172\" align=\"center\">LoginName<\/td>\n<td width=\"488\">SQL Server login name of the client.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">12<\/td>\n<td width=\"172\" align=\"center\">SPID<\/td>\n<td width=\"488\">Server Process ID assigned by SQL Server to the process associated with the client.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">13<\/td>\n<td width=\"172\" align=\"center\">Duration<\/td>\n<td width=\"488\">Amount of elapsed time (in milliseconds) taken by the event. This data column is not populated by the Hash Warning event.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">14<\/td>\n<td width=\"172\" align=\"center\">StartTime<\/td>\n<td width=\"488\">Time at which the event started, when available.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">15<\/td>\n<td width=\"172\" align=\"center\">EndTime<\/td>\n<td width=\"488\">Time at which the event ended. This column is not populated for starting event classes, such as SQL:BatchStarting or SP:Starting. It is also not populated by the Hash Warning event.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">16<\/td>\n<td width=\"172\" align=\"center\">Reads<\/td>\n<td width=\"488\">Number of logical disk reads performed by the server on behalf of the event. This column is not populated by the Lock:Released event.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">17<\/td>\n<td width=\"172\" align=\"center\">Writes<\/td>\n<td width=\"488\">Number of physical disk writes performed by the server on behalf of the event.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">18<\/td>\n<td width=\"172\" align=\"center\">CPU<\/td>\n<td width=\"488\">Amount of CPU time (in milliseconds) used by the event.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">26<\/td>\n<td width=\"172\" align=\"center\">ServerName<\/td>\n<td width=\"488\">Name of the instance of SQL Server (either servername or servername\\instancename) being traced.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">35<\/td>\n<td width=\"172\" align=\"center\">DatabaseName<\/td>\n<td width=\"488\">Name of the database specified in the USE database statement.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">40<\/td>\n<td width=\"172\" align=\"center\">DBUserName<\/td>\n<td width=\"488\">SQL Server database username of the client.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">48<\/td>\n<td width=\"172\" align=\"center\">RowCounts<\/td>\n<td width=\"488\">Number of rows in the batch.<\/td>\n<\/tr>\n<tr>\n<td width=\"81\" align=\"center\">64<\/td>\n<td width=\"172\" align=\"center\">SessionLoginName<\/td>\n<td width=\"488\">The login name of the user who originated the session. For example, if you connect to SQL Server using Login1 and execute a statement as Login2, SessionLoginName displays Login1, while LoginName displays Login2. This data column displays both SQL Server and Windows logins.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>A lista completa com todos os eventos pode ser encontrada no <em>books online<\/em> pesquisando pela procedure <em>sp_trace_setevent<\/em>.<\/p>\n<p>Em seguida \u00e9 realizado o filtro na coluna 13(<em>Duration<\/em>) para retornar apenas os valores maiores ou iguais a 3 segundos. Segue abaixo o <em>script<\/em> dessa procedure.<\/p>\n<pre class=\"lang:tsql decode:true \">\r\nCREATE PROCEDURE [dbo].[stpCreate_Trace]\r\nAS\r\nBEGIN\r\n    DECLARE @rc INT, @TraceID INT, @maxfilesize BIGINT, @on BIT, @intfilter INT, @bigintfilter BIGINT\r\n    SELECT @on = 1, @maxfilesize = 50\r\n    \r\n    -- Cria\u00e7\u00e3o do trace\r\n    EXEC @rc = [dbo].[sp_trace_create] @TraceID OUTPUT, 0, N'C:\\Trace\\Querys_Demoradas', @maxfilesize, NULL\r\n    \r\n    IF (@rc != 0) GOTO error\r\n    \r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 1,  @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 6,  @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 8,  @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 10, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 11, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 12, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 13, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 14, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 15, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 16, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 17, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 18, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 26, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 35, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 40, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 48, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 10, 64, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 1,  @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 6,  @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 8,  @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 10, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 11, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 12, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 13, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 14, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 15, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 16, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 17, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 18, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 26, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 35, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 40, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 48, @on\r\n    EXEC [dbo].[sp_trace_setevent] @TraceID, 12, 64, @on\r\n    \r\n    SET @bigintfilter = 3000000 -- 3 segundos\r\n    \r\n    EXEC [dbo].[sp_trace_setfilter] @TraceID, 13, 0, 4, @bigintfilter\r\n    \r\n    -- Set the trace status to start\r\n    EXEC [dbo].[sp_trace_setstatus] @TraceID, 1\r\n    \r\n    GOTO finish\r\n    \r\n    error:\r\n\t\tSELECT ErrorCode = @rc\r\n    finish:\r\nEND\r\n<\/pre>\n<p>No meu principal servidor de produ\u00e7\u00e3o, utilizo 3 segundos para realizar o filtro, mas isso depende da realidade de cada ambiente. Por exemplo, antigamente eram logadas as <em>querys<\/em> que demoravam mais de 10 segundos no meu ambiente, depois da migra\u00e7\u00e3o para o SQL Server 2005 e da aquisi\u00e7\u00e3o de um <em>storage<\/em> baixamos para 5 segundos. H\u00e1 pouco tempo, depois de melhorar as <em>querys<\/em> mais demoradas e as que mais consumiam recursos, ap\u00f3s a altera\u00e7\u00e3o, cria\u00e7\u00e3o e exclus\u00e3o de muitos indices (tudo isso com a ajuda das salvadoras <em>dmvs<\/em> presentes a partir do SQL Server 2005), como s\u00f3 estavam sendo retornadas 30 <em>querys<\/em> por hora em m\u00e9dia, baixei o <em>trace<\/em> para 3 segundos para melhor identificar poss\u00edveis gargalos. O <em>tuning<\/em> continua, para que eu tenha cada vez menos registros demorando mais de 3 segundos.<\/p>\n<p>Agora vamos rodar nossa <em>procedure<\/em> para criar o <em>trace<\/em>.<\/p>\n<pre class=\"lang:tsql decode:true \">\r\nEXEC [dbo].[stpCreate_Trace]\r\n<\/pre>\n<p>Para conferir o <em>trace<\/em> criado, basta executar a query abaixo.<\/p>\n<pre class=\"lang:tsql decode:true \">\r\nSELECT *\r\nFROM :: fn_trace_getinfo(default)\r\nWHERE CAST([value] AS VARCHAR(50)) = 'C:\\Trace\\Querys_Demoradas.trc'\r\n<\/pre>\n<p>Depois de criado, vamos rodar algumas <em>querys<\/em> para testar o nosso trace.<\/p>\n<pre class=\"lang:tsql decode:true \">\r\nWAITFOR DELAY '00:00:04'\r\n\r\nGO\r\nCREATE PROCEDURE [dbo].[stpTeste_Trace1]\r\nAS\r\nBEGIN\r\n    WAITFOR DELAY '00:00:04'\r\nEND\r\n\r\nGO\r\nEXEC [dbo].[stpTeste_Trace1]\r\n\r\n-- Conferindo todos os dados que foram armazenados no trace.\r\nSELECT\t[Textdata], [NTUserName], [HostName], [ApplicationName], [LoginName], [SPID], CAST([Duration] \/ 1000\/ 1000.00 AS NUMERIC(15, 2)) [Duration],\r\n\t\t[Starttime], [EndTime], [Reads],[writes], [CPU], [Servername], [DatabaseName], [rowcounts], [SessionLoginName]\r\nFROM :: fn_trace_gettable('C:\\Trace\\Querys_Demoradas.trc', default)\r\nWHERE [Duration] IS NOT NULL\r\nORDER BY [Starttime]\r\n<\/pre>\n<p>Segue abaixo o resultado da <em>query<\/em> com os dados armazenados no<em> trace<\/em>.<\/p>\n<p><a href=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2010\/10\/resultadoquerytrace5b65d.jpg\" rel=\"WLPP\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; border-width: 0;\" title=\"Resultado Query Trace\" src=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2010\/10\/resultadoquerytrace5b65d.jpg?w=300\" alt=\"Resultado Query Trace\" width=\"952\" height=\"53\" border=\"0\" \/><\/a><\/p>\n<p>A coluna <em>TextData<\/em> retorna exatamente a <em>query<\/em> que voc\u00ea rodou e demorou mais de 3 segundos. Esse tempo pode ser conferido na coluna <em>Duration<\/em>, <em>Starttime<\/em> e <em>Endtime<\/em>. Outras 3 colunas que me ajudam muito s\u00e3o as colunas <em>Reads<\/em>, <em>Writes<\/em> e <em>CPU<\/em> que mostram os recursos que uma determinada <em>query<\/em> est\u00e1 consumindo.<\/p>\n<p>Este artigo continua em <a title=\"passo-a-passo-para-encontrar-as-querys-mais-demoradas-do-banco-de-dados-parte-2\/\" href=\"https:\/\/www.fabriciolima.net\/blog\/2010\/06\/05\/passo-a-passo-para-encontrar-as-querys-mais-demoradas-do-banco-de-dados-parte-2\/\" target=\"_blank\">Passo a passo para encontrar as querys mais demoradas do Banco de Dados &#8211; Parte 2<\/a>.<\/p>\n<p><b>Gostou dessa dica?<\/b><\/p>\n<p>Cadastre seu e-mail para receber novos Posts e curta minha <a href=\"https:\/\/www.facebook.com\/FabricioLimaSolucoesemBancodeDados\/\" target=\"_blank\">P\u00e1gina no Facebook<\/a> para receber Dicas de Leituras e Eventos sobre SQL Server.<\/p>\n<p>Aprenda a criar essa e outras rotinas para administrar melhor seu SQL Server no meu <a href=\"https:\/\/www.fabriciolima.net\/cursos-online\/treinamento-tarefas-do-dia-a-dia-de-um-dba-online\/\" target=\"_blank\">Treinamento de Tarefas do Dia a Dia de um DBA<\/a>.<\/p>\n<p>Abra\u00e7os,<\/p>\n<p>Fabr\u00edcio Lima<\/p>\n<p>MCITP \u2013 Database Administrator<\/p>\n<p>Consultor e Instrutor SQL Server<\/p>\n<p>Trabalha com SQL Server desde 2006\n<\/p><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Fala Pessoal, Quem n\u00e3o quer saber quais s\u00e3o as querys mais demoradas do seu ambiente de Banco de Dados? Em quais hor\u00e1rios essas querys rodam? Quem est\u00e1 executando? De onde est\u00e1 sendo rodada? Quais os recursos que essas querys consomem? Nesse artigo, demonstrarei como eu fa\u00e7o esse acompanhamento no meu ambiente de trabalho. Ele \u00e9 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"footnotes":""},"categories":[3,280],"tags":[18,23,26,65,63,48,66,31,64,49,33,50,34,35,67,40],"class_list":["post-293","post","type-post","status-publish","format-standard","hentry","category-administracao-de-bd","category-virtual-pass-br","tag-checklist","tag-dba","tag-excel","tag-lentidao","tag-performance","tag-planilha","tag-problema","tag-profile","tag-query-lenta","tag-sql","tag-sql-server","tag-sql-server-2005","tag-sql-server-2008","tag-sql-server-2008-r2","tag-sql-server-passo-a-passo","tag-tuning"],"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/293","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/comments?post=293"}],"version-history":[{"count":0,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/293\/revisions"}],"wp:attachment":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/media?parent=293"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/categories?post=293"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/tags?post=293"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}