{"id":206,"date":"2010-12-25T11:27:34","date_gmt":"2010-12-25T13:27:34","guid":{"rendered":"http:\/\/fabriciodba.wordpress.com\/?p=206"},"modified":"2017-02-04T14:33:04","modified_gmt":"2017-02-04T16:33:04","slug":"casos-do-dia-a-dia-diminuindo-um-problema-de-memoria-no-sql-server","status":"publish","type":"post","link":"https:\/\/fabriciolima.net\/blog\/2010\/12\/25\/casos-do-dia-a-dia-diminuindo-um-problema-de-memoria-no-sql-server\/","title":{"rendered":"Casos do Dia a Dia &#8211; Diminu\u00edndo um problema de mem\u00f3ria no SQL Server"},"content":{"rendered":"<div id=\"_mcePaste\" class=\"mcePaste\" style=\"position: absolute; width: 1px; height: 1px; overflow: hidden; top: 0px; left: -10000px;\">\ufeff<\/div>\n<p>Fala Pessoal,<\/p>\n<p>Em meu ambiente de trabalho, possuo um servidor 32 bits com o SQL Server 2005 32 bits,\u00a0com 4 GB de mem\u00f3ria e com a op\u00e7\u00e3o \/3GB habilitada. Esse servidor possui uma m\u00e9dia de 1500 conex\u00f5es simult\u00e2neas no banco de dados durante o hor\u00e1rio de pico e possui 200 Gb de dados. Repetindo, 200 GB de dados e 4 GB de mem\u00f3ria.<\/p>\n<p>Como pode ser visto na figura abaixo, o servi\u00e7o do SQL Server consegue utilizar 2.51 GB de mem\u00f3ria nesse servidor.<br \/>\n<a href=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/3.png\"><\/a><\/p>\n<p><a href=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/3.png\"><\/a><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-209\" title=\"3\" src=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/3.png\" alt=\"\" width=\"274\" height=\"57\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/3.png 274w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/3-100x21.png 100w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/3-20x4.png 20w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/3-272x57.png 272w\" sizes=\"auto, (max-width: 274px) 100vw, 274px\" \/><a href=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/2.jpg\"><\/a><\/p>\n<p>Essa informa\u00e7\u00e3o foi gerada com a query abaixo:<\/p>\n<p>select counter_name ,cntr_value,cast((cntr_value\/1024.0)\/1024.0 as numeric(8,2)) as Gb<br \/>\nfrom sys.dm_os_performance_counters<br \/>\nwhere counter_name like &#8216;%server_memory%&#8217;;<\/p>\n<p>Voc\u00eas devem concordar comigo que 2.51 GB de mem\u00f3ria \u00e9 muito pouco para a quantidades de dados que possuo. Com a query abaixo \u00e9 poss\u00edvel verificar o contador Page Life Expectancy, que \u00e9 a expectativa de vida em segundos de uma p\u00e1gina de dados na mem\u00f3ria. No meu ambiente, esse contador tinha uma m\u00e9dia entre 30 e 40\u00a0 segundos durante o dia, sendo que um n\u00famero consider\u00e1vel aceit\u00e1vel para esse contador \u00e9 acima de 300 segundos.<\/p>\n<p>SELECT cntr_value AS &#8216;Page Life Expectancy&#8217;<br \/>\nFROM sys.dm_os_performance_counters<br \/>\nWHERE object_name = &#8216;SQLServer:Buffer Manager&#8217;<br \/>\nAND counter_name = &#8216;Page life expectancy&#8217;<\/p>\n<p>Resultado:<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-208\" title=\"2\" src=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/2.jpg\" alt=\"\" width=\"152\" height=\"36\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/2.jpg 152w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/2-150x36.jpg 150w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/2-100x24.jpg 100w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/2-20x5.jpg 20w\" sizes=\"auto, (max-width: 152px) 100vw, 152px\" \/><\/p>\n<p>Antes de realizar uma reestrutura\u00e7\u00e3o nos meus \u00edndices (criar missed index, alterar o fillfactor, excluir \u00edndices duplicados e excluir indices n\u00e3o utilizados), esse contador tinha uma m\u00e9dia\u00a0abaixo de 20 segundos em determinadas horas do dia.<\/p>\n<p>Buscando uma forma de amenizar esse problema(at\u00e9 que seja realizado um upgrade de Hardware), dei uma boa pesquisada sobre mem\u00f3ria e verifiquei que desses 2.51 GB de mem\u00f3ria, o SQL Server estava utilizando 1.5 GB de mem\u00f3ria para outros tipos de cache diferentes do Data Cache (Buffer Pool).<\/p>\n<p>Olha que absurdo, 1.5 GB para alguns caches (Ex: Caches de Plano de Execu\u00e7\u00e3o de Procedures e de Adhoc Querys)\u00a0 e apenas 1 GB para o Buffer Pool, onde realmente est\u00e3o as paginas de dados que s\u00e3o utilizadas por qualquer manipula\u00e7\u00e3o de dados no SQL Server.<br \/>\nQuando voc\u00ea realiza uma consulta, caso as p\u00e1ginas de dados que sua consulta deveria retornar n\u00e3o estejam no Buffer Pool, as p\u00e1ginas s\u00e3o lidas do disco, armazenadas no Buffer Pool e em seguida s\u00e3o retornadas para a sua consulta. Ou seja, quanto maior for o tamanho do Buffer Pool, mais p\u00e1ginas de dados ser\u00e3o armazenadas na mem\u00f3ria, menos leituras ser\u00e3o realizadas no disco e, consequentemente, o ganho de performance ser\u00e1 muito grande.<\/p>\n<p>Executando as querys abaixo, foi poss\u00edvel identificar o quanto de mem\u00f3ria estava sendo utilizado por alguns tipos de Cache do SQL Server:<\/p>\n<p>&#8212; Utiliza\u00e7\u00e3o por tipo de cache<br \/>\nSELECT\u00a0 type, SUM(single_pages_kb)\/1024. AS [SPA Mem, MB],SUM(Multi_pages_kb)\/1024. AS [MPA Mem,MB]\nFROM sys.dm_os_memory_clerks<br \/>\nGROUP BY type<br \/>\nHAVING\u00a0 SUM(single_pages_kb) + sum(Multi_pages_kb)\u00a0 &gt; 40000 &#8212; S\u00f3 os maiores consumidores de mem\u00f3ria<br \/>\nORDER BY SUM(single_pages_kb) DESC<\/p>\n<p>&#8212; Total utilizado<br \/>\nSELECT\u00a0 SUM(single_pages_kb)\/1024. AS [SPA Mem, KB],SUM(Multi_pages_kb)\/1024. AS [MPA Mem, KB]\nFROM sys.dm_os_memory_clerks<\/p>\n<p>A figura abaixo mostra como estava o resultado dessas querys no meu ambiente no hor\u00e1rio de produ\u00e7\u00e3o.<\/p>\n<p><a href=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/4.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-210\" title=\"4\" src=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/4.jpg?w=300\" alt=\"\" width=\"300\" height=\"113\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/4.jpg 325w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2010\/12\/4-300x113.jpg 300w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>O CACHESTTORE_OBJCP\u00a0 \u00e9 o cache das Stored procedures, Triggers e Functions.<\/p>\n<p>O CACHESTORE_SQLCP \u00e9 o cache de Ad-hoc queries e n\u00e3o \u00e9 muito reutilizado pelo SQL Server, pois para uma mesma consulta com par\u00e2metros diferentes, s\u00e3o gerados dois planos de execu\u00e7\u00f5es diferentes.<\/p>\n<p>O USERSTORE_TOKENOERM \u00e9 o cache que armazena v\u00e1rias informa\u00e7\u00f5es de seguran\u00e7a que s\u00e3o utilizadas pela Engine do SQL Server.<\/p>\n<p>Analisando essas informa\u00e7\u00f5es, tomei as seguintes medidas:<\/p>\n<p>Como o CACHESTTORE_OBJCP \u00e9 muito reutilizado, n\u00e3o mechi no mesmo pois poderia piorar ainda mais meu problema de performance. Se no meu servidor tivesse a vers\u00e3o do SQL Server 2008, eu poderia excluir individualmente do cache alguns planos de execu\u00e7\u00e3o que ocupam mais espa\u00e7o na mem\u00f3ria e s\u00e3o poucos utilizados. Mas como ainda est\u00e1 na vers\u00e3o do SQL Server 2005, n\u00e3o tive o que fazer.<\/p>\n<p>J\u00e1 os caches CACHESTORE_SQLCP e USERSTORE_TOKENOERM, que ocupavam juntos mais de 1 GB de mem\u00f3ria, me ajudariam bastante liberando mem\u00f3ria para o Buffer Pool. Entretanto, limpando o cache CACHESTORE_SQLCP eu aumentaria meu consumo de CPU pois incrementaria o n\u00famero de compila\u00e7\u00f5es de algumas querys. Todavia, como meu gargalo \u00e9 mem\u00f3ria, resolvi limpar esses caches assim mesmo.<\/p>\n<p>Criei uma Stored Procedure que roda de 3 em 3 minutos para realizar a limpeza desses caches\u00a0 e ainda armazeno um log da frequencia com que esses caches s\u00e3o limpos em uma tabela para hist\u00f3rico conforme os scripts abaixo:<\/p>\n<p>CREATE TABLE [dbo].[Log_Limpeza_Cache](<br \/>\n[Id_Log] [int] IDENTITY(1,1) ,<br \/>\n[Dt_Limpeza] [datetime] ,<br \/>\n[Tipo_Cache] [varchar](30) ,<br \/>\n[Tamanho_MB] [numeric](15, 2) )<\/p>\n<p>CREATE PROCEDURE [dbo].[stpLimpa_Memory_Cache]\nAS<br \/>\nBEGIN<br \/>\nDECLARE\u00a0@USERSTORE_TOKENPERM numeric(15,2), @CACHESTORE_SQLCP numeric(15,2)<br \/>\nSELECT @CACHESTORE_SQLCP = SUM(single_pages_kb+Multi_pages_kb)\/1024.<br \/>\nFROM sys.dm_os_memory_clerks<br \/>\nWHERE type = &#8216;CACHESTORE_SQLCP&#8217;<\/p>\n<p>SELECT @USERSTORE_TOKENPERM = SUM(single_pages_kb+Multi_pages_kb)\/1024.<br \/>\nFROM sys.dm_os_memory_clerks<br \/>\nWHERE type = &#8216;USERSTORE_TOKENPERM&#8217;<\/p>\n<p>IF @USERSTORE_TOKENPERM &gt; 30<br \/>\nbegin<br \/>\ninsert into Log_Limpeza_Cache(Dt_Limpeza,Tipo_Cache,Tamanho_MB)<br \/>\nselect getdate(), &#8216;USERSTORE_TOKENPERM&#8217;, @USERSTORE_TOKENPERM<br \/>\nDBCC FREESYSTEMCACHE(&#8216;TokenAndPermUserStore&#8217;)<br \/>\nend<br \/>\nIF @CACHESTORE_SQLCP &gt; 60<br \/>\nbegin<br \/>\ninsert into Log_Limpeza_Cache(Dt_Limpeza,Tipo_Cache,Tamanho_MB)<br \/>\nselect getdate(), &#8216;CACHESTORE_SQLCP&#8217;, @CACHESTORE_SQLCP<\/p>\n<p>DBCC FREESYSTEMCACHE(&#8216;SQL Plans&#8217;)<br \/>\nend<\/p>\n<p>END<\/p>\n<p>Ap\u00f3s a primeira limpeza do cache(momentos de tens\u00e3o), fiquei acompanhando meu <a title=\"passo-a-passo-para-encontrar-as-querys-mais-demoradas-do-banco-de-dados-parte-1\/\" href=\"https:\/\/www.fabriciolima.net\/blog\/2010\/06\/05\/passo-a-passo-para-encontrar-as-querys-mais-demoradas-do-banco-de-dados-parte-1\/\" target=\"_blank\">trace com as querys mais demoradas\u00a0do banco de dados<\/a> para ver se teria algum problema de performance. Entretanto, isso n\u00e3o aconteceu. Pelo contr\u00e1rio, a quantidade de querys lentas diminu\u00edram.<\/p>\n<p>Ap\u00f3s alguns minutos, analisando os contadores de performance, o Page life Expectancy passou a ter uma m\u00e9dia entre 100 e 120 segundos. Simplesmente triplicou.<\/p>\n<p>Meu buffer pool, aumentou para mais de 2 GB, ou seja, a quantidade de dados que o SQL Server disponibiliza em mem\u00f3ria para ser utilizada sem acessar o disco dobrou. Eu verifico essa informa\u00e7\u00e3o com a query abaixo:<\/p>\n<p>SELECT DB_NAME(database_id) AS [Database Name],<br \/>\nCOUNT(*) * 8\/1024.0 AS [Cached Size (MB)]\nFROM sys.dm_os_buffer_descriptors<br \/>\nWHERE database_id &gt; 4 &#8211;\u2013- exclude system databases<br \/>\nAND database_id &lt;&gt; 32767&#8211; \u2013- exclude ResourceDB<br \/>\nGROUP BY DB_NAME(database_id)<br \/>\nORDER BY [Cached Size (MB)] DESC;<\/p>\n<p>Como j\u00e1 era esperado, tamb\u00e9m tive um aumento de processamento e de recompiles, mas o benef\u00edcio da libera\u00e7\u00e3o de mem\u00f3ria foi maior para o meu ambiente. O n\u00famero de procedimentos demorando mais de 3 segundos diminu\u00edu consideravelmente.<\/p>\n<p>Al\u00e9m dessa altera\u00e7\u00e3o, fiz muitas outras altera\u00e7\u00f5es de performance para esse servidor aguentar o grande aumento de acesso que temos no Banco de Dados nesse Natal, mas acredito que essa altera\u00e7\u00e3o foi\u00a0a que fez a maior diferen\u00e7a e me proporcionou um Natal bem tranquilo. Um excelente presente de Natal.<\/p>\n<p>Claro que antes de\u00a0realizar essas altera\u00e7\u00f5es no meu servidor de produ\u00e7\u00e3o, testei em outros servidores menos cr\u00edticos. Deixo essa experi\u00eancia para quem possui um ambiente com esse mesmo tipo de problema. Espero que possa ter ajudado.<\/p>\n<p>&nbsp;<\/p>\n<p><b>Gostou dessa Dica?<\/b><\/p>\n<p>Curta, comente, compartilhe\u2026<\/p>\n<p>Assine meu canal no <a href=\"https:\/\/www.youtube.com\/channel\/UCeBRAO_LLrUdSrOXIywjzRA\" target=\"_blank\">Youtube<\/a> e curta minha p\u00e1gina no <a class=\"external-link wpel-icon-left\" href=\"https:\/\/www.facebook.com\/FabricioLimaSolucoesemBancodeDados\/\" target=\"_blank\" rel=\"nofollow external noopener noreferrer\" data-wpel-link=\"external\">Facebook<\/a> para receber Dicas de Leituras, V\u00eddeos e Eventos sobre SQL Server.<\/p>\n<p>Confira mais experi\u00eancias do Dia a Dia de um DBA 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>At\u00e9 a pr\u00f3xima.<\/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>\ufeff Fala Pessoal, Em meu ambiente de trabalho, possuo um servidor 32 bits com o SQL Server 2005 32 bits,\u00a0com 4 GB de mem\u00f3ria e com a op\u00e7\u00e3o \/3GB habilitada. Esse servidor possui uma m\u00e9dia de 1500 conex\u00f5es simult\u00e2neas no banco de dados durante o hor\u00e1rio de pico e possui 200 Gb de dados. Repetindo, [&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,6,280],"tags":[75,119,121,124,123,23,84,126,25,127,122,80,29,120,63,128,49,33,81,40,125],"class_list":["post-206","post","type-post","status-publish","format-standard","hentry","category-administracao-de-bd","category-casos-do-dia-a-dia","category-virtual-pass-br","tag-3gb","tag-buffer-manager","tag-cache-sql-server","tag-cachestore_sqlcp","tag-cachesttore_objcp","tag-dba","tag-dbcc","tag-dbcc-freesystemcache","tag-dmv","tag-dm_os_buffer_descriptors","tag-dm_os_memory_clerks","tag-dm_os_performance_counters","tag-memoria","tag-page-life-expectancy","tag-performance","tag-problema-memoria","tag-sql","tag-sql-server","tag-target-server-memory","tag-tuning","tag-userstore_tokenoerm"],"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/206","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=206"}],"version-history":[{"count":0,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/206\/revisions"}],"wp:attachment":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/media?parent=206"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/categories?post=206"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/tags?post=206"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}