{"id":2836,"date":"2014-03-06T08:48:27","date_gmt":"2014-03-06T10:48:27","guid":{"rendered":"http:\/\/fabriciolima.net\/blog\/?p=2836"},"modified":"2016-07-23T13:29:36","modified_gmt":"2016-07-23T16:29:36","slug":"o-transaction-log-de-uma-transacao-e-liberado-quando-dou-um-rollback","status":"publish","type":"post","link":"https:\/\/fabriciolima.net\/blog\/2014\/03\/06\/o-transaction-log-de-uma-transacao-e-liberado-quando-dou-um-rollback\/","title":{"rendered":"O Transaction Log de uma transa\u00e7\u00e3o \u00e9 liberado quando dou um rollback?"},"content":{"rendered":"<p>Ol\u00e1 Pessoal,<\/p>\n<p>Imagina que voc\u00ea executa um update em milh\u00f5es de linhas no seu banco de dados (com BEGIN TRAN) e v\u00ea o Log crescer v\u00e1rios GBs. Ao concluir a transa\u00e7\u00e3o, voc\u00ea verifica que fez coisa errada e decide dar um rollback. Todo esse log que foi armazenado ser\u00e1 liberado?<\/p>\n<p>A resposta \u00e9 n\u00e3o.<\/p>\n<p>Segue um teste para comprovar isso em uma base com o recovery FULL.<\/p>\n<p><!--more--><\/p>\n<p>Criei uma tabela e populei com 40 mil linhas:<\/p>\n<p>CREATE TABLE TesteRollbackLog(<br \/>\nCod INT IDENTITY,<br \/>\nDt_Log DATETIME DEFAULT(GETDATE()),<br \/>\nDs_Log CHAR(8000))<\/p>\n<p>INSERT INTO TesteRollbackLog(Ds_Log)<br \/>\nSELECT &#8216;Fabricio Lima Consultoria SQL Server&#8217;<br \/>\nGO 40000<\/p>\n<p>Em seguida fiz um backup do log para ele iniciar os testes limpo:<\/p>\n<p>CHECKPOINT<br \/>\nBACKUP LOG ConsultoriaSQLServer TO DISK = &#8216;C:\\Temp\\ConsultoriaSQLServer_Log.bak&#8217;<\/p>\n<p>Ser\u00e3o realizadas algumas opera\u00e7\u00f5es na tabela TesteRollbackLog e a cada opera\u00e7\u00e3o, armazenarei o tamanho do arquivo de log em uma tabela chamada\u00a0Tamanho_Log.<\/p>\n<p>CREATE TABLE [dbo].[Tamanho_Log](<br \/>\nTp_Teste varchar(1000),<br \/>\n[Log Size (KB)] [bigint] NOT NULL,<br \/>\n[Log Used (KB)] [bigint] NOT NULL,<br \/>\n[Log Used %] [decimal](22, 2) NULL<br \/>\n) ON [PRIMARY]\n<p>Para facilitar o insert nessa tabela de Log com alguns marcadores, criei a procedure abaixo:<\/p>\n<p>CREATE PROCEDURE stpCarga_Tamanho_log @tp_Teste varchar(1000)<br \/>\nAS<br \/>\nINSERT INTO [Tamanho_Log] (Tp_Teste,[Log Size (KB)],[Log Used (KB)],[Log Used %])<br \/>\nSELECT @tp_Teste,ls.cntr_value AS [Log Size (KB)] , lu.cntr_value AS [Log Used (KB)] ,<br \/>\nCAST(CAST(lu.cntr_value AS FLOAT) \/ case when CAST(ls.cntr_value AS FLOAT) = 0 then 1<br \/>\nelse CAST(ls.cntr_value AS FLOAT) end AS DECIMAL(18,2)) * 100 AS [Log Used %]\nFROM sys.databases AS db<br \/>\nINNER JOIN sys.dm_os_performance_counters AS lu ON db.name = lu.instance_name<br \/>\nINNER JOIN sys.dm_os_performance_counters AS ls ON db.name = ls.instance_name<br \/>\nWHERE lu.counter_name LIKE &#8216;Log File(s) Used Size (KB)%&#8217;<br \/>\nAND ls.counter_name LIKE &#8216;Log File(s) Size (KB)%&#8217;<br \/>\nAND db.[name] = &#8216;ConsultoriaSQLServer&#8217;<\/p>\n<p><strong>Criadas as estruturas, vamos aos testes.<\/strong><\/p>\n<p>Os seguintes scripts foram executados nessa ordem:<\/p>\n<p>&#8212; Tamanho inicial do Log<br \/>\nEXEC stpCarga_Tamanho_log &#8216;Inicio&#8217;<\/p>\n<p>&#8212; Executado 3 vezes com os par\u00e2metros Teste1, Teste2 e Teste3<br \/>\nDECLARE @Teste VARCHAR(50) SET @Teste = &#8216;Teste3&#8217;<br \/>\nUPDATE TesteRollbackLog SET Ds_log = &#8216;Fabricio Lima Consultoria e Treinamento SQL Server &#8211; &#8216;+@Teste<br \/>\nEXEC stpCarga_Tamanho_log @Teste<\/p>\n<p>&#8212; Realiza\u00e7\u00e3o do update com Begin Tran para o teste do COMMIT<br \/>\nDECLARE @Teste VARCHAR(50) SET @Teste = &#8216;BEGIN TRAN &#8211; COMMIT &#8216;<br \/>\nBEGIN TRAN UPDATE TesteRollbackLog SET Ds_log = &#8216;Fabricio Lima Consultoria e Treinamento SQL Server &#8211; &#8216;+@Teste<br \/>\nEXEC stpCarga_Tamanho_log @Teste<\/p>\n<p>&#8212; Commit da transa\u00e7\u00e3o anterior<br \/>\nCOMMIT<br \/>\nEXEC stpCarga_Tamanho_log &#8216;COMMIT&#8217;<\/p>\n<p>&#8212; Realiza\u00e7\u00e3o do update com Begin Tran para o teste do ROLLBACK<br \/>\nDECLARE @Teste VARCHAR(50)<br \/>\nSET @Teste = &#8216;BEGIN TRAN &#8211; ROLLBACK&#8217;<br \/>\nBEGIN TRAN UPDATE TesteRollbackLog SET Ds_log = &#8216;Fabricio Lima Consultoria e Treinamento SQL Server &#8211; &#8216;+@Teste<br \/>\n&#8211;EXEC stpCarga_Tamanho_log &#8216;BEGIN TRAN &#8211; ROLLBACK&#8217; &#8212; executar em outra conex\u00e3o para n\u00e3o dar rollback desse insert no log<\/p>\n<p>&#8212; Rollback da Transa\u00e7\u00e3o<br \/>\nROLLBACK<br \/>\nEXEC stpCarga_Tamanho_log &#8216;ROLLBACK&#8217;<\/p>\n<p>Analisando o resultado disso tudo na tabela Tamanho_Log:<\/p>\n<p style=\"text-align: center;\"><a href=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/ResultTestesLog.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-2837 aligncenter\" title=\"ResultTestesLog\" src=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/ResultTestesLog.png\" alt=\"\" width=\"429\" height=\"174\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/ResultTestesLog.png 429w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/ResultTestesLog-300x122.png 300w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/ResultTestesLog-410x166.png 410w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/ResultTestesLog-100x41.png 100w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/ResultTestesLog-275x112.png 275w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/ResultTestesLog-20x8.png 20w\" sizes=\"auto, (max-width: 429px) 100vw, 429px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>\u00c9 poss\u00edvel perceber que os 3 primeiros updates nos 40 mil registros da tabela ocuparam aproximadamente 3.8 MB no log.<\/p>\n<p>Ap\u00f3s realizar o update com o BEGIN TRAN (linha 4 para 5), o SQL Server pr\u00e9-alocou quase 20 MB de log. Quando eu confirmei a transa\u00e7\u00e3o com o COMMIT (linha 5 para 6), ele liberou esse espa\u00e7o que estava pr\u00e9-alocado e passou a ocupar apenas 23 MB de tamanho (tinha alcan\u00e7ado 37 MB).<\/p>\n<p>Realizando o mesmo update com BEGIN TRAN (linha 6 para linha 7), o SQL novamente pr\u00e9-alocou o log com quase 18 MB de espa\u00e7o. Entretanto, ao executar um rollback (linha 7 para 8), o comportamento do Log foi completamente diferente, pois o SQL Server n\u00e3o liberou aquele espa\u00e7o pr\u00e9-alocado, e sim passou a utilizar quase todo esse espa\u00e7o que j\u00e1 estava ocupado no Log, liberando menos de 0,5 MB (linha 7 para 8).<\/p>\n<p><strong>Conclus\u00e3o:<\/strong> Se voc\u00ea executar um Update e seu log crescer 20 GB, ao executar um commit, ele vai diminuir pois j\u00e1 foi pr\u00e9-alocado um espa\u00e7o para a realiza\u00e7\u00e3o de um rollback no Log. Se voc\u00ea der rollback na transa\u00e7\u00e3o, os 20GB ser\u00e3o utilizados quase em toda sua totalidade.<\/p>\n<p>Essa pr\u00e9-aloca\u00e7\u00e3o do Log tamb\u00e9m acontece quando n\u00e3o usamos o BEGIN TRAN, conforme veremos no teste abaixo.<\/p>\n<p><strong>Segundo Cen\u00e1rio<\/strong><\/p>\n<p>Com um arquivo de log de tamanho 20 MB, mas que n\u00e3o pode mais crescer, realizei dois updates de 40 mil linhas na tabela.<\/p>\n<p>&#8212; Marca inicial do Teste<br \/>\nEXEC stpCarga_Tamanho_log &#8216;Inicio Segundo Teste&#8217;<\/p>\n<p>&#8212; Update executado com sucesso<br \/>\nDECLARE @Teste VARCHAR(50) SET @Teste = &#8216;Teste1&#8217;<br \/>\nUPDATE TesteRollbackLog SET Ds_log = &#8216;Fabricio Lima Consultoria e Treinamento SQL Server &#8211; &#8216;+@Teste<br \/>\nEXEC stpCarga_Tamanho_log @Teste<\/p>\n<p>&#8212; Transaction Log FULL no segundo update<br \/>\nDECLARE @Teste VARCHAR(50) SET @Teste = &#8216;Teste2&#8217;<br \/>\nUPDATE TesteRollbackLog SET Ds_log = &#8216;Fabricio Lima Consultoria e Treinamento SQL Server &#8211; &#8216;+@Teste<br \/>\nEXEC stpCarga_Tamanho_log @Teste<\/p>\n<p>Erro na segunda tentativa de realizar o update na tabela:<\/p>\n<p><a href=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/LogFull.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" title=\"LogFull\" src=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/LogFull.png\" alt=\"\" width=\"750\" height=\"147\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Olhando novamente a tabela de Log:<\/p>\n<p><a href=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/SegundoTeste.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-2842 aligncenter\" title=\"SegundoTeste\" src=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/SegundoTeste.png\" alt=\"\" width=\"395\" height=\"59\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/SegundoTeste.png 395w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/SegundoTeste-300x45.png 300w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/SegundoTeste-100x15.png 100w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/SegundoTeste-275x41.png 275w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/SegundoTeste-20x3.png 20w\" sizes=\"auto, (max-width: 395px) 100vw, 395px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>\u00c9 poss\u00edvel perceber que no primeiro update, o Log cresceu 3.7 MB (linha 1 para 2).<\/p>\n<p>Dessa forma, seria poss\u00edvel executar mais alguns \u00a0updates ainda at\u00e9 chegar nos 20 MB de tamanho m\u00e1ximo do Log, certo?<a href=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2014\/03\/LogFull.png\"><br \/>\n<\/a><\/p>\n<p>Errado. Como vimos, no primeiro teste, o SQL Server pr\u00e9-aloca uma por\u00e7\u00e3o do Log para que consiga dar rollback em caso de problema na transa\u00e7\u00e3o. Nesse caso, o SQL tinha 14 Mb para realizar a transa\u00e7\u00e3o, mas ele precisava alocar mais que isso e a transa\u00e7\u00e3o falhou por LOG FULL (mesmo sem o begin tran especificado como vimos no primeiro cen\u00e1rio).<\/p>\n<p>Por que o SQL Server faz isso?<\/p>\n<p>Caso o SQL Server n\u00e3o fizesse isso, em caso de rollback o log poderia precisar crescer mais e n\u00e3o conseguiria, o que levaria a transa\u00e7\u00e3o a ficar inconsistente, deixando a base OFFLINE imediatamente e posteriormente com o status SUSPECT. O SQL Server previne que esse problema aconte\u00e7a.<\/p>\n<p>Onde vi isso?<\/p>\n<p>No treinamento do Paul Randal na <a href=\"http:\/\/www.pluralsight.com\/training\" target=\"_blank\">Pluralsight<\/a>. Vi ele falando, quis realizar alguns testes para ver na pr\u00e1tica como acontecia e aproveitei para divulgar aqui no Blog.<\/p>\n<p>Caso algu\u00e9m tenha uma interpreta\u00e7\u00e3o dos testes diferente da minha, pe\u00e7o que inclua nos coment\u00e1rios para agregar valor ao post.<\/p>\n<p><b>Gostou desse Post?<\/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>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<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ol\u00e1 Pessoal, Imagina que voc\u00ea executa um update em milh\u00f5es de linhas no seu banco de dados (com BEGIN TRAN) e v\u00ea o Log crescer v\u00e1rios GBs. Ao concluir a transa\u00e7\u00e3o, voc\u00ea verifica que fez coisa errada e decide dar um rollback. Todo esse log que foi armazenado ser\u00e1 liberado? A resposta \u00e9 n\u00e3o. Segue [&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,33,280],"tags":[312,1108,1109,1106,23,368,1104,1110,33,1107,116,1105],"class_list":["post-2836","post","type-post","status-publish","format-standard","hentry","category-administracao-de-bd","category-sql-server","category-virtual-pass-br","tag-arquivo-de-log","tag-begin-tran","tag-commit","tag-como-funciona-o-log","tag-dba","tag-log","tag-log-full","tag-rollback","tag-sql-server","tag-trancasao-sql-server","tag-transaction-log","tag-transaction-log-full"],"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/2836","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=2836"}],"version-history":[{"count":0,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/2836\/revisions"}],"wp:attachment":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/media?parent=2836"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/categories?post=2836"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/tags?post=2836"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}