{"id":10154,"date":"2019-02-19T09:16:05","date_gmt":"2019-02-19T12:16:05","guid":{"rendered":"http:\/\/www.fabriciolima.net\/?p=10154"},"modified":"2020-10-07T08:36:51","modified_gmt":"2020-10-07T11:36:51","slug":"melhorando-a-performance-de-consultas-no-totvs-rm-apos-uma-migracao-para-o-sql-server-2016","status":"publish","type":"post","link":"https:\/\/fabriciolima.net\/blog\/2019\/02\/19\/melhorando-a-performance-de-consultas-no-totvs-rm-apos-uma-migracao-para-o-sql-server-2016\/","title":{"rendered":"Melhorando a performance de consultas no TOTVS RM ap\u00f3s uma migra\u00e7\u00e3o para o SQL Server 2016"},"content":{"rendered":"<p>Fala Pessoal,<\/p>\n<p>Hoje vou compartilhar para voc\u00eas um caso real e interessante que passamos ao migrar um banco de dados <strong>TOTVS RM<\/strong> do SQL Server 2008 para o SQL Server 2016.<\/p>\n<p>Fizemos a migra\u00e7\u00e3o e, como j\u00e1 estamos acostumados, algumas consultas passaram a ficar lentas ap\u00f3s a migra\u00e7\u00e3o, mesmo sem mudar o n\u00edvel de compatibilidade das bases.<\/p>\n<p>Nesse ambiente, o sistema ainda era antigo e estavam usando o <strong>BDE<\/strong> para se comunicar com o banco de dados.<\/p>\n<p>As queries com problema batiam no banco da forma abaixo com um <strong>sp_executesql<\/strong>:<\/p>\n<p><strong><em>&#8220;exec sp_executesql N&#8217;SELECT A.CODCOLIGADA, A.NUMEROCONTA, A.SEQUENCIALCONTA, A.SEQPARCIAL, A.SEQDEBITOMATMED, A.IDPRD, A.FATORCONVERSAOMATMED, colunas from tabelas WHERE filtros&#8221;<\/em><\/strong><\/p>\n<p>Essa query demorava <strong>5 minutos<\/strong> quando rodava na aplica\u00e7\u00e3o. Mas quando eu pegava o mesmo c\u00f3digo\/par\u00e2metros dela no profile para rodar manualmente, ela executava em <strong>0 (zero) segundos<\/strong>.<\/p>\n<p><strong>Fabr\u00edcio, esse \u00e9 aquele caso que o plano delas est\u00e3o diferentes, por isso essa diferen\u00e7a no tempo, certo?\u00a0\u00a0<\/strong><\/p>\n<p>Certo. Os planos est\u00e3o diferentes.<\/p>\n<p>S\u00f3 tem um pequeno grande detalhe: A query n\u00e3o pode ser alterada na aplica\u00e7\u00e3o.<\/p>\n<p>O milagre tem que ser feito s\u00f3 no SQL Server.<\/p>\n<p><strong>Como podemos fazer isso? Query Store?<\/strong><\/p>\n<p>Query Store \u00e9 um dos benef\u00edcios que temos quando migramos para o SQL Server 2016 ou superior.<\/p>\n<p>Eu o utilizei para analisar algumas coisas, mas as queries tinham o query_id diferentes no Query Store. N\u00e3o era 1 query com dois planos diferentes, onde damos um simples clique no Query Store para for\u00e7ar o plano mais r\u00e1pido e resolvemos nosso problema.<\/p>\n<p>Mesmo com o mesmo c\u00f3digo e par\u00e2metros, l\u00e1 no query store eram duas consultas completamente diferentes.<\/p>\n<p><strong>Como isso \u00e9 poss\u00edvel Fabr\u00edcio?<\/strong><\/p>\n<p>Isso acontece devido as\u00a0<strong>SET OPTIONS<\/strong>\u00a0que a aplica\u00e7\u00e3o est\u00e1 usando na conex\u00e3o com o banco.<\/p>\n<p>Rodei essa query abaixo para conferir o Plan_handle da query:<\/p>\n<pre class=\"lang:tsql decode:true\">SELECT deqs.plan_handle,dest.text\r\nFROM   sys.dm_exec_query_stats as deqs\r\nCROSS  APPLY sys.dm_exec_sql_text(deqs.sql_handle) as dest\r\nWHERE  dest.text like '%SELECT A.CODCOLIGADA, A.NUMEROCONTA, A.SEQUENCIALCONTA, A.SEQPARCIAL%'\r\norder by deqs.total_logical_reads desc\r\n<\/pre>\n<p>No resultado podemos ver a mesma query com o plan_handle diferente (destacado em vermelho):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-10160 aligncenter\" src=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_1.png\" alt=\"\" width=\"1015\" height=\"231\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_1.png 1690w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_1-300x68.png 300w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_1-1024x233.png 1024w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_1-768x175.png 768w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_1-1536x350.png 1536w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_1-700x159.png 700w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_1-410x93.png 410w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_1-100x23.png 100w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_1-275x63.png 275w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_1-20x5.png 20w\" sizes=\"auto, (max-width: 1015px) 100vw, 1015px\" \/><\/p>\n<p>Em seguida, joguei os 2 plan_handles para uma tabela tempor\u00e1ria para validar as <strong>SET OPTIONS<\/strong> desses planos:<\/p>\n<pre class=\"lang:tsql decode:true\">drop table if exists #PlanHandles\r\n\r\nSELECT distinct deqs.plan_handle\r\ninto #PlanHandles\r\nFROM   sys.dm_exec_query_stats as deqs\r\nCROSS  APPLY sys.dm_exec_sql_text(deqs.sql_handle) as dest\r\nWHERE  dest.text like '%SELECT A.CODCOLIGADA, A.NUMEROCONTA%'\r\nand dest.text not like '%dm_exec_query_stats%'\r\n\r\nSELECT decp.plan_handle, epa.attribute, epa.[value], deqp.query_plan,*\r\nFROM sys.dm_exec_cached_plans as decp  \r\nOUTER APPLY sys.dm_exec_plan_attributes(decp.plan_handle) AS epa  \r\ninner join #PlanHandles as ph on ph.plan_handle = decp.plan_handle\r\nCROSS APPLY sys.dm_exec_query_plan(decp.plan_handle) as deqp\r\nWHERE epa.attribute in ('set_options') ;  \r\n<\/pre>\n<p>Resultado da query?<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-10161 aligncenter\" src=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_2.png\" alt=\"\" width=\"641\" height=\"314\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_2.png 933w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_2-300x147.png 300w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_2-768x376.png 768w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_2-700x343.png 700w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_2-410x201.png 410w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_2-100x49.png 100w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_2-275x135.png 275w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_2-20x10.png 20w\" sizes=\"auto, (max-width: 641px) 100vw, 641px\" \/><\/p>\n<p>Repare que a coluna <strong>value<\/strong> dos 2 planos destacados est\u00e3o com valores diferentes.<\/p>\n<p>Jogando esses valores na query m\u00e1gica que tem no Treinamento do <a href=\"https:\/\/blogfabiano.com\/\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>Fabiano Amorim<\/strong><\/a>:<\/p>\n<pre class=\"lang:tsql decode:true\">declare @set_options int = 266491\r\n\r\nif ((1 &amp; @set_options) = 1) print 'ANSI_PADDING'\r\nif ((4 &amp; @set_options) = 4) print 'FORCEPLAN'\r\nif ((8 &amp; @set_options) = 8) print 'CONCAT_NULL_YIELDS_NULL'\r\nif ((16 &amp; @set_options) = 16) print 'ANSI_WARNINGS'\r\nif ((32 &amp; @set_options) = 32) print 'ANSI_NULLS'\r\nif ((64 &amp; @set_options) = 64) print 'QUOTED_IDENTIFIER'\r\nif ((128 &amp; @set_options) = 128) print 'ANSI_NULL_DFLT_ON'\r\nif ((256 &amp; @set_options) = 256) print 'ANSI_NULL_DFLT_OFF'\r\nif ((512 &amp; @set_options) = 512) print 'NoBrowseTable'\r\nif ((4096 &amp; @set_options) = 4096) print 'ARITHABORT'\r\nif ((8192 &amp; @set_options) = 8192) print 'NUMERIC_ROUNDABORT'\r\nif ((16384 &amp; @set_options) = 16384) print 'DATEFIRST'\r\nif ((32768 &amp; @set_options) = 32768) print 'DATEFORMAT'\r\nif ((65536 &amp; @set_options) = 65536) print 'LanguageID'<\/pre>\n<p>Primeiramente joguei o valor <strong>266491:<\/strong><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" wp-image-10159 aligncenter\" src=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_3.png\" alt=\"\" width=\"581\" height=\"452\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_3.png 682w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_3-300x234.png 300w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_3-410x319.png 410w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_3-100x78.png 100w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_3-275x214.png 275w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_3-20x16.png 20w\" sizes=\"auto, (max-width: 581px) 100vw, 581px\" \/><\/p>\n<p>Podem ver que temos<strong> CONCAT_NUL_YIELDS_NULL<\/strong> e <strong>ARITHABORT<\/strong> setado para esse plano.<\/p>\n<p>Rodando a mesma query para o valor\u00a0<strong>262387<\/strong>:<\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" wp-image-10158 aligncenter\" src=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_4.png\" alt=\"\" width=\"463\" height=\"354\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_4.png 622w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_4-300x230.png 300w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_4-410x314.png 410w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_4-100x77.png 100w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_4-275x210.png 275w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_4-20x15.png 20w\" sizes=\"auto, (max-width: 463px) 100vw, 463px\" \/><\/p>\n<p>As op\u00e7\u00f5es\u00a0<strong>ARITHABORT<\/strong> e\u00a0<strong>CONCAT_NULL_YIELDS_NULL<\/strong> n\u00e3o est\u00e3o habilitadas para esse outro plano. Esse \u00e9 o plano que estava lendo.<\/p>\n<p>Abrindo uma conex\u00e3o no SSMS e deixando essas op\u00e7\u00f5es como OFF, eu consegui rodar a query e ver ela demorando 5 minutos. Massa!<\/p>\n<p><strong>E agora Fabr\u00edcio? Query Store n\u00e3o resolve, o que vamos fazer?<\/strong><\/p>\n<p>Vamos para a solu\u00e7\u00e3o raiz utilizando <strong>PLAN GUIDES<\/strong>.<\/p>\n<p>Por algum motivo, ap\u00f3s a migra\u00e7\u00e3o,\u00a0 o SQL Server estava estimando algo muito errado no plano quando vem com essas op\u00e7\u00f5es desligadas.<\/p>\n<p>Criei um <strong>Plan Guide<\/strong> para que compilasse essa query novamente ao ser executada e pronto! Problema resolvido!<\/p>\n<p>Segue o Plan Guide criado:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-10157 aligncenter\" src=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_5.png\" alt=\"\" width=\"966\" height=\"139\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_5.png 1174w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_5-300x43.png 300w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_5-1024x147.png 1024w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_5-768x111.png 768w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_5-700x101.png 700w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_5-410x59.png 410w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_5-100x14.png 100w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_5-275x40.png 275w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2019\/01\/post_RM_5-20x3.png 20w\" sizes=\"auto, (max-width: 966px) 100vw, 966px\" \/><\/p>\n<p><strong>A Query que estava rodando em 5 minutos foi para 0 (zero) segundos. <\/strong><\/p>\n<p>O cliente tomou um susto quando executou novamente.<\/p>\n<p>Tive que fazer isso para outras 5 queries do <strong>RM<\/strong> que estavam travando pelo mesmo motivo.<\/p>\n<p>E isso galera!!! Essa solu\u00e7\u00e3o resolveu o problema do <strong>TOTVS RM<\/strong>, mas isso tamb\u00e9m acontece com <strong>outras aplica\u00e7\u00f5es<\/strong>.<\/p>\n<p>Espero que tenham curtido.<\/p>\n<p>Segue um outro post onde falo sobre algumas\u00a0 dicas de migra\u00e7\u00e3o:<\/p>\n<p><a href=\"https:\/\/www.fabriciolima.net\/blog\/2017\/05\/23\/migrando-um-sql-server-2008-totvs-protheus-para-o-sql-server-2016-standard\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/www.fabriciolima.net\/blog\/2017\/05\/23\/migrando-um-sql-server-2008-totvs-protheus-para-o-sql-server-2016-standard\/<\/a><\/p>\n<p>Fica aqui um agradecimento ao <a href=\"https:\/\/blogfabiano.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">Rodrigo Ribeiro<\/a> e <a href=\"https:\/\/blogfabiano.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">Fabiano Amorim<\/a> que debateram esse problema comigo no Slack do <strong>#TeamFabricioLima<\/strong>.<\/p>\n<p>Ao Fabiano, um obrigado pela confer\u00eancia que fez comigo para discutir e testar a solu\u00e7\u00e3o do problema. <strong>Que time \u00e9 esse!!!<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p><strong>Atualizado no dia 07\/10\/2020:<\/strong><\/p>\n<p>Publiquei um curso com 11 horas de dura\u00e7\u00e3o com toda minha experi\u00eancia de anos no assunto e de dezenas de clientes Protheus atendidos:<\/p>\n<p>Curso: <a href=\"https:\/\/cursos.powertuning.com.br\/course?courseid=melhorando-a-performance-de-consultas-no-totvs-protheus\">Melhorando a Performance de Consultas no Totvs Protheus<\/a><\/p>\n<p>Gravei uma aula gr\u00e1tis com 60 minutos de dura\u00e7\u00e3o sobre o que voc\u00ea deve aprender para melhorar a performance no Protheus:<\/p>\n<p><iframe loading=\"lazy\" title=\"O que aprender para melhorar a performance no TOTVS Protheus\" width=\"1170\" height=\"658\" src=\"https:\/\/www.youtube.com\/embed\/BKmUa3aZn6s?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe><\/p>\n<p>&nbsp;<\/p>\n<p><b>Gostou da dica?<\/b><\/p>\n<p>Curta, comente, compartilhe com os coleguinhas\u2026<\/p>\n<p>Siga-nos no <a href=\"https:\/\/www.linkedin.com\/company\/fabriciolimasolucoesembd\/\" target=\"_blank\" rel=\"noopener noreferrer\">Linkedin<\/a>, <a href=\"https:\/\/www.youtube.com\/channel\/UCeBRAO_LLrUdSrOXIywjzRA\" target=\"_blank\" rel=\"noopener noreferrer\">Youtube<\/a>, <a href=\"https:\/\/www.facebook.com\/FabricioLimaSolucoesemBancodeDados\/\" target=\"_blank\" rel=\"noopener noreferrer\">Facebook<\/a> e <a href=\"https:\/\/www.instagram.com\/fabriciolimasolucoesembd\/\" target=\"_blank\" rel=\"noopener noreferrer\">Instagram<\/a> para receber dicas de leitura e eventos sobre SQL Server.<\/p>\n<p>Abra\u00e7os,<\/p>\n<p>Fabr\u00edcio Lima.<\/p>\n<p>Microsoft Data Platform MVP<\/p>\n<p>Consultor e Instrutor SQL Server<\/p>\n<p>Trabalha com SQL Server desde 2006<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Fala Pessoal, Hoje vou compartilhar para voc\u00eas um caso real e interessante que passamos ao migrar um banco de dados TOTVS RM do SQL Server 2008 para o SQL Server 2016. Fizemos a migra\u00e7\u00e3o e, como j\u00e1 estamos acostumados, algumas consultas passaram a ficar lentas ap\u00f3s a migra\u00e7\u00e3o, mesmo sem mudar o n\u00edvel de compatibilidade [&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,942,991,33,280],"tags":[1697,1696,1698,65,1677,1676,1392,1694,1516,63,1695,1464,1534,1015,1675],"class_list":["post-10154","post","type-post","status-publish","format-standard","hentry","category-administracao-de-bd","category-casos-do-dia-a-dia","category-consultoria-sql-server","category-dba-remoto","category-sql-server","category-virtual-pass-br","tag-arithabort","tag-bde","tag-concat_null_yields_null","tag-lentidao","tag-lentidao-rm","tag-lentidao-totvs","tag-migracao","tag-migracao-rm","tag-migracao-sql-server","tag-performance","tag-plan-guide","tag-query-store","tag-rm","tag-totvs","tag-totvs-rm"],"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/10154","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=10154"}],"version-history":[{"count":10,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/10154\/revisions"}],"predecessor-version":[{"id":13792,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/10154\/revisions\/13792"}],"wp:attachment":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/media?parent=10154"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/categories?post=10154"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/tags?post=10154"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}