Arquivo

Archive for the ‘Administração de BD’ Category

Casos do Dia a Dia – Diminuíndo um problema de memória no SQL Server

25 de dezembro de 2010 9 comentários

 

Esse post foi transferido para o novo domínio do Blog. Clique aqui para acessá-lo.

Anúncios

Casos do Dia a Dia – Você sabia que um arquivo de Log do SQL Server se fragmenta?

19 de dezembro de 2010 2 comentários

Fala Pessoal,

Vou ser sincero com vocês, até pouco tempo eu não tinha a menor idéia que um arquivo de log do SQL Server se fragmentava. Nunca tinha lido sobre o assunto. Entretanto, para isso que servem os mais 100 Blogs de SQL Server cadastrados no meu google reader e os vários profissionais SQL Server que sigo no twitter e que também divulgam dicas de excelentes artigos, whitepapers e etc.

Pouco tempo atrás, alguém “twitou” esse excelente artigo 8 Steps to better Transaction Log throughput.

Nesse artigo Kimberly L. Tripp (Blog|Twitter) da uma série de dicas para serem aplicadas nos arquivos de log do SQL Server.  Uma dessas dicas (a de número oito) é para checar e corrigir a fragmentação interna desses arquivos. Ela ainda disse que essa dica resolveu um problema de um cliente na Turquia.

Caso o seu arquivo de log tenha sofrido muitos autogrowths, ele pode ficar internamente fragmentado. O arquivo de log do SQL Server é dividido em pequenos pedaços chamados VLF(Virtual Log Files) que crescem a medida que seu arquivo de log cresce. Segundo Kimberly, geralmente, a maioria dos arquivos de logs devem ter somente entre 20 e 30 VLF. 50 ainda é aceitável dependendo do tamanho do seu arquivo de log.

Um número excessivo de VLF pode causar um impacto negativo em todas as atividades do transaction log e além disso, ainda pode ocorrer uma degradação da performance enquanto um backup do log está sendo executado. Para conferir quantos VLF você tem em uma database, basta verificar o número de linhas retornadas pelo comando DBCC LOGINFO conforme a figura abaixo:

Nesse caso, o arquivo de log dessa database possui 4 VLFs.

Seguindo o procedimento do artigo, realizei os seguintes passos para desfragmentar o arquivo de log:

 1 – Esperei por um período de pouco movimento e limpei o transaction log realizando um backup do mesmo. Caso você utilize o recovery model SIMPLE, você não precisa realizar um backup do log, ao invés disso, você limpará o transaction log rodando um CHECKPOINT.

            BACKUP LOG databasename TO devicename

2 – Realizei um Shrink no log para que ele ficasse com o menor tamanho possível (por isso que ele é limpo no passo 1).

            DBCC SHRINKFILE(transactionloglogicalfilename, TRUNCATEONLY)

3 – Alterei o arquivo de log da database para um tamanho apropriado onde ele necessite crescer apenas em alguns casos raros.

            ALTER DATABASE databasename
            MODIFY FILE
            ( NAME = transactionloglogicalfilename,
              SIZE = newtotalsize ) 

No meu ambiente eu possuía uma database com 275 VLF e as maiores estavam todas com mais de 150 VLF. Após realizar os passos acima, todas as databases estão com menos de 20 VLF, sendo que dentre elas, mantenho arquivos de log com 4 GB devido a algumas operações e manutenções que ocorrem durante a madrugada.

Com relação a performance, em um dos meus servidores, faço um backup do log a cada 7 minutos para minhas 5 principais databases, com isso, possuo um grande número de backups do log. Como eu monitoro toda query que demora mais de 3 segundos, constantemente a query que fazia backup do log demorava para ser executada e era armazenada no meu log. Após a realização dos passos descritos acima, é muito raro um backup do log  entrar nesse trace. Essa melhora foi bem perceptível no meu ambiente.

Então, cabe a vocês verificarem a quantidade de VLF de suas databases pois é um procedimento bem simples.

Abraços,

Fabrício França Lima



Casos do Dia a Dia – Exclusão de um índice grande e pouco utilizado

30 de novembro de 2010 5 comentários

Fala pessoal, 

Algum tempo atrás, compartilhei uma experiência que tive no Blog do Fabrício Catae (Blog|Twitter), mas também resolvi deixar registrado por aqui.

Muitos de vocês já utilizaram a dmv sys.dm_db_index_usage_stats para verificar a utilização e atualização dos índices de uma tabela. Também sabemos que essa dmv tem seus dados reiniciados quando o serviço do SQL Server é reiniciado. Assim, como os servidores do meu ambiente de banco de dados possuem uma atualização mensal de segurança, os servidores são reiniciados mensalmente e os dados dessa dmv seriam perdidos. Ou seja, eu só teria essas valiosas informações sobre os índices durante o período de um mês.

Para resolver esse problema, criei uma tabela que armazena diariamente a utilização dos índices. Com isso, posso analisar durante um período muito grande, a utilização dos meus índices antes de excluí-los. Eu já possuo mais de 1 ano de baseline já que o espaço ocupado por essas informações é pequeno.

Para quem quiser possuir um histórico dessas informações, o script abaixo cria uma tabela de histórico e insere as informações de utilização dos índices nessa tabela.

CREATE TABLE [dbo].[Historico_Utilizacao_Indices](
                               [Id_Historico_Utilizacao_Indices] [int] IDENTITY(1,1) NOT NULL,
                               [Dt_Historico] [datetime] NULL,
                               [Nm_Servidor] [varchar](30) NULL,
                               [Nm_Database] [varchar](30) NULL,
                               [Nm_Tabela] [varchar](50) NULL,
                               [Nm_Indice] [varchar](50) NULL,
                               [User_Seeks] [int] NULL,
                               [User_Scans] [int] NULL,
                               [User_Lookups] [int] NULL,
                               [User_Updates] [int] NULL,
                               [Ultimo_Acesso] [datetime] NULL )

INSERT INTO Historico_Utilizacao_Indices(Dt_Historico, Nm_Servidor, Nm_Database, Nm_Tabela, Nm_Indice,  User_Seeks, User_Scans,User_Lookups, User_Updates, Ultimo_acesso)
SELECT getdate(), @@SERVERNAME, db_name(db_Id()), o.Name, i.name, s.user_seeks,s.user_scans,s.user_lookups, s.user_Updates,  isnull(s.last_user_seek,isnull(s.last_user_scan,s.last_User_Lookup)) Ultimo_acesso
FROM sys.dm_db_index_usage_stats s
             join sys.indexes i on i.object_id = s.object_id and i.index_id = s.index_id
             join sys.sysobjects o on i.object_id = o.id
WHERE s.database_id = db_id()
ORDER BY o.Name, i.name, s.index_id

Para armazenar as informações, criei um job que roda essa query de INSERT para cada database que eu defini guardar os históricos.

Assim, eu utilizo essa tabela de histórico para analisar uma possível exclusão dos índices que são pouco utilizados ou que não são utilizados. Agora, compartilhando a experiência que tive, eu possuo uma tabela com muitas consultas e alterações que armazena 50 milhões de registros. Nessa tabela, tenho um índice em um campo chamado Fl_Situacao que pode possuir os valores 0, 1, 2, 3 ou 4.

Sempre acompanhei esse índice e verifiquei que tinha algumas utilizações somente no inicio do mês. Um certo dia, resolvi excluir esse índice seguindo o raciocínio de que o índice era pouco seletivo, a tabela é muito grande e o índice era pouco utilizado, não valendo a pena o custo de manutenção do mesmo. Após excluir o índice, acompanhando meu trace com as querys que demoram mais de 3 segundos, verifiquei que nenhuma query apresentou problema de lentidão.

Show de bola, diminuí uma operação de manutenção de um índice em uma tabela muito utilizada.

Entretanto, no inicio do mês, existia uma query com uma condição “where Fl_Situacao = 2” dentre outras restrições. Quando essa query rodou sem o índice que eu excluí, a mesma fez um clustered index scan nessa tabela, me causando um grande problema de lentidão no banco de dados. Isso aconteceu pois, dos 50 milhões de registros existentes na tabela, apenas 1.000 registros possuíam o campo Fl_Situacao = 2, o que tornava o índice existente nessa coluna extremamente eficiente para essa query. 

Resultado, como não dava para alterar a consulta, tive que recriar o índice na mesma noite.

Mais uma vez eu digo, vivendo e aprendendo!!! Meu maior aprendizado está no meu dia a dia de trabalho.

Abraços, 

Fabrício França Lima

SQL Server 11 – Codinome Denali

14 de novembro de 2010 6 comentários

 

Esse post foi transferido para o novo domínio do Blog. Clique aqui para acessá-lo.

 



Quanto tempo levaria para descobrir uma database corrompida?

3 de novembro de 2010 Deixe um comentário

Fala Pessoal,

Hoje saiu a edição 18 da resvista Codificando.NET e-magazine e com um artigo meu publicado. =)

O título do meu artigo é: Quanto tempo levaria para descobrir uma database corrompida?

Não deixem de ler!!! Segue o link para a realização do download grátis da Revista: Codificando.Net

Agradeço a Fernanda Sallai (Blog) pelo convite.

Fabrício França Lima demonstra Quanto tempo levaria para descobrir uma database corrompida.

Anderson Castro faz uma Introdução ao Windows Phone 7 Series.

Fernando Gonçalves ensina como Autenticar usuários em ASP.NET Web Forms.

Fabrício Sanchez aborda sobre Visual Studio LightSwitch – É bom, mas é preciso entendê-lo!.

Ítalo Chesley explica o Entity Framework: Trabalhando com Model First.

Everton José Benedicto escreve sobre SQL Server Integration Services (SSIS).

Alliston Carlos apresenta O novo Visual Studio LightSwitch.

Antonio Lucas Finotti Pereira explica como Gerar Gráficos com Silverlight, WCF e LINQ.

Leandro Alves Santos demonstra como Reutilizar Código Nativo no .NET.

Alexandre Tarifa relata E se foi mais um Tech·Ed na sua coluna .Close()

 

Abraços,

Fabrício França Lima



Utilização de mais de 3GB de memória no SQL Server em um ambiente 32 bits

9 de agosto de 2010 5 comentários
 Esse post foi transferido para o novo domínio do Blog. Clique aqui para acessá-lo.

Eu preciso dar um sp_recompile em uma procedure após alterá-la?

25 de julho de 2010 4 comentários

Fala Pessoal,

Você já houviu alguém dizer que quando se altera uma procedure devemos dar um sp_recompile nessa SP?

Até essa semana eu acreditava realmente que isso era necessário. O argumento que eu houvi quando aprendi e que eu mesmo respondia para alguém quando era questionado sobre o motivo de executar a sp_recompile em uma procedure após alterá-la era o seguinte:

“Quando alteramos uma procedure devemos executar o sp_recompile nessa procedure para forçarmos uma recompilação do seu plano de execução que pode ter sido alterado com a manutenção que foi realizada nessa procedure.”

Realmente, faz todo o sentido, pois ao alterarmos uma procedure podemos incluir ou excluir uma query, o que com certeza altera o plano de execução dessa procedure. Entretanto, realizando a leitura do meu livro SQL Server 2008 Internals, eu li que a execução do comando ALTER PROCEDURE exclui o plano dessa procedure do cache. Na mesma hora veio a voz do Silvio Santos na minha cabeça: “Ma ma eu só acreditoooo… veeeendoo, hi hiiii”.Então subi minha Vm para realizar meus testes.

Inicialmente, criei uma tabela e populei a mesma com 1000 registros.

CREATE TABLE Venda(
      Id_Venda INT IDENTITY(1,1),
      Dt_Venda DATETIME,
      Vl_Venda NUMERIC(15,2)
      )

GO
INSERT INTO Venda(Dt_Venda,Vl_Venda)
SELECT GETDATE(),CAST(1000000*RAND() AS INT)%1000
GO 1000
GO
CREATE CLUSTERED INDEX SK01_Venda ON Venda(Id_Venda)
CREATE NONCLUSTERED INDEX SK02_Venda ON Venda(Vl_Venda)include(Dt_Venda)

Em seguida criei duas procedures para realizar os testes.

CREATE PROCEDURE stpConsulta_Vendas @Vl_Venda NUMERIC(15,2)
AS
SELECT Id_Venda,Dt_Venda,Vl_Venda
FROM Venda
WHERE Vl_Venda >= @Vl_Venda

GO

CREATE PROCEDURE stpConsulta_Vendas_2 @Vl_Venda NUMERIC(15,2)
AS
EXEC stpConsulta_Vendas @Vl_Venda

Com a query abaixo podemos ver quais os planos de execução estão em cache. Chamarei essa query de QUERY A para referenciar as execuções posteriores da mesma.

SELECT Text, Plan_Handle, Size_in_bytes, Usecounts
FROM sys.dm_Exec_cached_plans AS cp
      CROSS APPLY sys.dm_exec_sql_text(plan_handle)
WHERE Text LIKE '%stpConsulta_Vendas%' -- para visualizar apenas o plano dessas procedures
      AND Text NOT LIKE '%dm_Exec_cached_plans%' --para não aparecer essa propria query
      AND Objtype = 'Proc' --Procedures
ORDER BY Size_in_bytes DESC

Neste momento, essa query não retorna nenhum registro. Entretanto, após a execução do script abaixo.

EXEC dbo.stpConsulta_Vendas 900.00
GO
EXEC dbo.stpConsulta_Vendas 800.50
GO
EXEC dbo.stpConsulta_Vendas_2 950.46
GO
EXEC dbo.stpConsulta_Vendas_2 990.25

Executando a QUERY A novamente temos o seguinte resultado.

FIGURA 1

Podemos claramente verificar no valor da coluna Usecounts que o mesmo plano de execução da stpConsulta_Vendas foi utilizado 4 vezes, sendo duas chamadas diretas e duas chamadas de dentro da stpConsulta_Vendas_2. Já o plano de execução da stpConsulta_Vendas_2 foi utilizado as duas vezes que foi chamado explicitamente.

Agora que é a hora da verdade, vamos alterar nossa procedure e ver se o planos de execução será eliminado do cache.

ALTER PROCEDURE stpConsulta_Vendas @Vl_Venda NUMERIC(15,2)
AS
SELECT Id_Venda,Dt_Venda,Vl_Venda
FROM Venda
WHERE Vl_Venda >= @Vl_Venda

Executando a Query A, temos o seguinte resultado.

FIGURA 2

Logo, o plano de execução da procedure stpConsulta_Vendas que havia sido executado 4 vezes foi removido do cache. Com isso, confirmamos o fato de que quando alteramos uma procedure, o plano de execução dessa procedure já é excluído do cache automaticamente, não necessitando assim de executarmos o comando sp_recompile para essa procedure.

Aproveitando, vamos executar o comando sp_recompile para a stoConsulta_Vendas_2 para visualizarmos que o plano de execução dessa procedure também será excluído do cache.

exec sp_recompile stpConsulta_Vendas_2

Executando novamente a QUERY A é possível identificar que o plano de execução dessa procedure também foi retirado do cache.

Em suma, a partir de agora nunca mais executarei um sp_recompile após alterar uma procedure. Vivendo e aprendendo.

Abraços,

Fabrício França Lima