{"id":928,"date":"2011-08-11T22:35:00","date_gmt":"2011-08-12T00:35:00","guid":{"rendered":"http:\/\/fabriciolima.net\/blog\/?p=928"},"modified":"2016-07-23T23:21:23","modified_gmt":"2016-07-24T02:21:23","slug":"como-monitorar-o-page-split-de-um-indice","status":"publish","type":"post","link":"https:\/\/fabriciolima.net\/blog\/2011\/08\/11\/como-monitorar-o-page-split-de-um-indice\/","title":{"rendered":"Como monitorar o Page Split de um \u00cdndice?"},"content":{"rendered":"<p>Fala Pessoal,<\/p>\n<p>H\u00e1 um tempo atr\u00e1s, dando uma vasculhada na internet a procura de como monitorar Page Split no meu Banco de Dados(SQL SERVER 2005), encontrei uma query EXCEPCIONAL em v\u00e1rios artigos em ingl\u00eas, mas tamb\u00e9m encontrei um artigo em portugu\u00eas do Laerte J\u00fanior(<a href=\"http:\/\/twitter.com\/#!\/LaerteSQLDBA\" target=\"_blank\">Twitter<\/a>) que fala sobre o assunto: <a title=\"Laerte Junior\" href=\"http:\/\/shellyourexperience.wordpress.com\/2009\/10\/05\/o-que-mais-o-transaction-log-pode-fazer-por-mim\/\" target=\"_blank\">O que mais o transaction log pode fazer por mim.<\/a><\/p>\n<p>Segue a query EXCEPCIONAL:<\/p>\n<p>select\u00a0\u00a0 allocUnitName,COUNT(*)<br \/>\nfrom\u00a0\u00a0\u00a0 ::fn_dblog(null, null)<br \/>\nwhere Operation = &#8216;LOP_DELETE_SPLIT&#8217;<br \/>\ngroup by allocUnitName<br \/>\norder by COUNT(*) desc<\/p>\n<p>Resumidamente, Page Split \u00e9 uma opera\u00e7\u00e3o que acontece quando um registro \u00e9 inserido ou alterado (expandindo seu tamanho) em uma p\u00e1gina de dados e essa p\u00e1gina n\u00e3o possui espa\u00e7o livre suficiente para realizar essa opera\u00e7\u00e3o. Assim, uma p\u00e1gina \u00e9 dividida em duas para comportar as novas informa\u00e7\u00f5es, o que causa uma opera\u00e7\u00e3o custosa de disco (nosso maior gargalo no banco de dados).<\/p>\n<p>Para tentar diminuir o Page Split do meu ambiente, criei uma rotina(que eu rodo somente quando necess\u00e1ria) que guarda um hist\u00f3rico das opera\u00e7\u00f5es de Page Splits realizadas nos meus \u00edndices.<\/p>\n<p>Inicialmente criei duas tabelas em uma database que armazenar\u00e1 os hist\u00f3ricos:<br \/>\nCREATE TABLE Max_LSN_Log(<br \/>\nNm_Database VARCHAR(50),<br \/>\nUltimo_LSN VARCHAR(46))<\/p>\n<p>CREATE TABLE Historico_Page_Split(<br \/>\nId_Historico_Page_Split INT IDENTITY(1,1),<br \/>\nNm_database VARCHAR(40),<br \/>\nNm_tabela VARCHAR(50),<br \/>\nNm_Indice VARCHAR(50),<br \/>\nDt_Referencia DATETIME DEFAULT(getdate()),<br \/>\nNr_Page_Split INT,<br \/>\nCONSTRAINT PK_Historico_Page_Split PRIMARY KEY(Id_Historico_Page_Split)<br \/>\n)<\/p>\n<p>Em seguida, voc\u00ea deve se conectar em cada database que voc\u00ea deseja realizar o monitoramento e executar os comandos abaixo:<\/p>\n<p>Inser\u00e7\u00e3o do menor LSN existente na tabela de controle do LSN para que n\u00e3o seja inseridas informa\u00e7\u00f5es duplicadas:<\/p>\n<p>INSERT INTO Max_LSN_Log<br \/>\nselect db_name(db_id()), min([Current LSN])<br \/>\nFROM ::fn_dblog(null, null)<br \/>\nWHERE Operation = N&#8217;LOP_DELETE_SPLIT&#8217;<br \/>\nAND parsename(AllocUnitName,3) &lt;&gt; &#8216;sys&#8217;<\/p>\n<p>Cria\u00e7\u00e3o da procedure que armazenar\u00e1 o hist\u00f3rico.<br \/>\nCREATE procedure stpCarga_Page_Split<br \/>\nAS<br \/>\nBEGIN<br \/>\ndeclare @Ultimo_LSN varchar(46)<\/p>\n<p>&#8212; Seleciona o \u00faltimo LSN registrado no Log<br \/>\nselect @Ultimo_LSN = Ultimo_LSN<br \/>\nfrom Max_LSN_Log<br \/>\nwhere Nm_database = db_name(db_id())<\/p>\n<p>insert into Historico_Page_Split(Nm_database,Nm_tabela,Nm_Indice,Nr_Page_Split)<br \/>\nselect db_name(db_id()),\u00a0 substring(replace(AllocUnitName,&#8217;dbo.&#8217;,&#8221;),1, CHARINDEX(&#8216;.&#8217;,replace(AllocUnitName,&#8217;dbo.&#8217;,&#8221;)) -1), &#8211;tabela<br \/>\nsubstring(replace(AllocUnitName,&#8217;dbo.&#8217;,&#8221;), CHARINDEX(&#8216;.&#8217;,replace(AllocUnitName,&#8217;dbo.&#8217;,&#8221;))+1,<br \/>\nlen( replace(AllocUnitName,&#8217;dbo.&#8217;,&#8221;) ) &#8211; CHARINDEX(&#8216;.&#8217;,replace(AllocUnitName,&#8217;dbo.&#8217;,&#8221;)) +1 ), &#8212; Indice,<br \/>\ncount([AllocUnitName]) [Splits]\nfrom ::fn_dblog(null, null)<br \/>\nwhere Operation = N&#8217;LOP_DELETE_SPLIT&#8217;<br \/>\nand parsename(AllocUnitName,3) &lt;&gt; &#8216;sys&#8217;<br \/>\nand [Current LSN] &gt; @Ultimo_LSN\u00a0 &#8212; Para n\u00e3o contabilizar duas vezes<br \/>\ngroup by\u00a0 substring(replace(AllocUnitName,&#8217;dbo.&#8217;,&#8221;),1, CHARINDEX(&#8216;.&#8217;,replace(AllocUnitName,&#8217;dbo.&#8217;,&#8221;)) -1), &#8211;tabela<br \/>\nsubstring(replace(AllocUnitName,&#8217;dbo.&#8217;,&#8221;), CHARINDEX(&#8216;.&#8217;,replace(AllocUnitName,&#8217;dbo.&#8217;,&#8221;))+1,<br \/>\nlen( replace(AllocUnitName,&#8217;dbo.&#8217;,&#8221;) ) &#8211; CHARINDEX(&#8216;.&#8217;,replace(AllocUnitName,&#8217;dbo.&#8217;,&#8221;)) +1 ) &#8212; Indice<\/p>\n<p>if @@rowcount &gt; 0 &#8211;atualiza o LSN da database<br \/>\nbegin<br \/>\nselect @Ultimo_LSN = max([Current LSN])<br \/>\nfrom ::fn_dblog(null, null)<br \/>\nwhere Operation = N&#8217;LOP_DELETE_SPLIT&#8217;<br \/>\nand parsename(AllocUnitName,3) &lt;&gt; &#8216;sys&#8217;<br \/>\nand ([Current LSN] &gt; @Ultimo_LSN or @Ultimo_LSN is null)<\/p>\n<p>update Max_LSN_Log<br \/>\nset Ultimo_LSN = @Ultimo_LSN<br \/>\nwhere Nm_database = db_name(db_id())<br \/>\nend<\/p>\n<p>END<\/p>\n<p>Tenho certeza que voc\u00ea realiza um Backup do Log de suas databases OLTP(eu rodo de 7 em 7 minutos para algumas DB&#8217;s), ent\u00e3o, agora \u00e9 s\u00f3 incluir a chamada dessa SP no Job que realiza o backup do Log das suas databases que ser\u00e3o logadas.\u00a0 Ela deve ser inclu\u00edda em um step antes da realiza\u00e7\u00e3o do Backup.<\/p>\n<p>Exec stpCarga_Page_Split<\/p>\n<p>Com a query abaixo podemos acompanhar o Page_Split dos \u00edndices de seu ambiente em um per\u00edodo espec\u00edfico:<\/p>\n<p>SELECT Nm_Database,Nm_Tabela,Nm_Indice, sum(Nr_Page_Split) Total_Page_Split<br \/>\nFROM Historico_Page_Split<br \/>\nWHERE Dt_Referencia &gt;= &#8216;20110810&#8217; and Dt_Referencia &lt; &#8216;20110811&#8217;<br \/>\nGROUP BY Nm_Database,Nm_Tabela,Nm_Indice<br \/>\nORDER BY Total_Page_Split DESC<\/p>\n<p>Com mais essa informa\u00e7\u00e3o em m\u00e3os, temos mais uma forma de analisar a famosa d\u00favida de qual \u00e9 o FILLFACTOR ideal para um \u00edndice. Diminuindo o FILLFACTOR de um \u00edndice voc\u00ea tamb\u00e9m diminui o PAGE SPLIT desse \u00edndice, em contra partida, tamb\u00e9m aumenta o espa\u00e7o em disco para armazenar o \u00edndice e consequentemente o espa\u00e7o utilizado em mem\u00f3ria, pois um n\u00famero maior de p\u00e1ginas ser\u00e1 carregada na mem\u00f3ria.<\/p>\n<p>Concordo que n\u00e3o \u00e9 simples realizar essa an\u00e1lise, entretanto, caso n\u00e3o queira entrar nesse n\u00edvel de detalhe do SGBD, n\u00f3s DBA&#8217;s estamos a disposi\u00e7\u00e3o para resolver esse e muitos outros tipos de problemas. =)<\/p>\n<p>Como nem tudo s\u00e3o flores, realizar a leitura do arquivo de log \u00e9 uma opera\u00e7\u00e3o MUITO MUITO custosa. Ent\u00e3o se seu ambiente j\u00e1 est\u00e1 com grandes problemas de performance, isso n\u00e3o \u00e9 indicado. Fa\u00e7a um teste com uma database com poucas altera\u00e7\u00f5es e depois passe\u00a0 para as outras databases caso seu ambiente n\u00e3o esteja sendo impactado. Isso fica por sua conta e risco.<\/p>\n<p>No meu ambiente eu habilitei essa procedure durante menos de uma hora para cada database e identifiquei os \u00edndices que mais possu\u00edam PAGE SPLIT durante esse per\u00edodo.<\/p>\n<p>Espero que possa te ajudar de alguma forma.<\/p>\n<p>Script deste post: <a href=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/downloads\/2011\/08\/Artigo-Page-Split.txt\" class=\"download\" target=\"_blank\">Artigo Page Split<\/a><\/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>Fala Pessoal, H\u00e1 um tempo atr\u00e1s, dando uma vasculhada na internet a procura de como monitorar Page Split no meu Banco de Dados(SQL SERVER 2005), encontrei uma query EXCEPCIONAL em v\u00e1rios artigos em ingl\u00eas, mas tamb\u00e9m encontrei um artigo em portugu\u00eas do Laerte J\u00fanior(Twitter) que fala sobre o assunto: O que mais o transaction log [&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":[118,23,350,365,367,110,370,369,368,371,300,366,364,63,49,33],"class_list":["post-928","post","type-post","status-publish","format-standard","hentry","category-administracao-de-bd","category-virtual-pass-br","tag-backup-log","tag-dba","tag-desempenho","tag-fillfactor","tag-fn_dblog","tag-indice","tag-ler-arquivo-log","tag-ler-log","tag-log","tag-lop_delete_split","tag-lsn","tag-monitoramento-page_split","tag-page-split","tag-performance","tag-sql","tag-sql-server"],"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/928","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=928"}],"version-history":[{"count":0,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/928\/revisions"}],"wp:attachment":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/media?parent=928"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/categories?post=928"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/tags?post=928"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}