Arquivo
Indice / Lookup / Recompile
Recentemente o Luciano Moreira publicou um post no seu blog perguntando porque as vezes o SQL Server não usa um determinado índice, aproveitando a deixa resolvi escrever um pouco sobre isso.
O link para o post do Luciano é este Exibir artigo…
DROP TABLE TMP
CREATE TABLE TMP(ID INT IDENTITY(1,1) PRIMARY KEY, NOME VARCHAR(200), Nome2 VarChar(200))
CREATE INDEX ix_Teste on TMP(Nome)
SET NOCOUNT ON
DECLARE @I INT
SET @I = 0
WHILE @I < 1000
BEGIN
INSERT INTO TMP(NOME, Nome2) VALUES(‘A’, NewID())
INSERT INTO TMP(NOME, Nome2) VALUES(‘B’, NewID())
INSERT INTO TMP(NOME, Nome2) VALUES(‘C’, NewID())
INSERT INTO TMP(NOME, Nome2) VALUES(‘D’, NewID())
INSERT INTO TMP(NOME, Nome2) VALUES(‘E’, NewID())
SET @I = @I + 1;
END
— Foram incluidos 5000 registros na tabela TMP
SELECT COUNT(*) FROM TMP
/*
Pressione CTRL-M para incluir o plano de execução no resultado da consulta e execute o comando abaixo.
O Select irá selecionar 1/5 dela
*/
SELECT * FROM TMP
WHERE Nome = ‘A’
/*
Repare que o SQL Server não utilizou o indice pela coluna Nome e achou
melhor fez um clustered scan, Vamos analisar melhor este comportamento.
Primeiramente vamos ver quantas páginas o SQL Server terá que ler para
retornar os dados da consulta, para isso vamos ligar o statistics io e rodar
o select novamente.
*/
SET STATISTICS IO ON
SELECT * FROM TMP
WHERE Nome = ‘A’
/*
Table ‘TMP’. Scan count 1, logical reads 37, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Podemos observar que o SQL leu 37 páginas de dados para retornar o resultado.
Agora vamos forçar o uso do indice por nome para fazer o Seek e não o Clustered Scan,
repare no plano de execução que o SQL fez um Seek e um bookmark para
pegar o valor da coluna Nome2 que não faz parte do indice nonclustered ix_teste.
No indice cluster(no nosso exemplo é o campo ID que é a primary key) temos as informações de todos
os dados da tabela, portanto sempre que uma consulta seleciona campos a mais do que os cobertos pelo
indice nonclustered o SQL irá no indice cluster ler esta informação.
No nosso exemplo abaixo isso aconteceu porque a coluna Nome2 não pertence ao indice nonclustered,
portanto o SQL fez o lookup para ler esta informação no indice cluster.
*/
SELECT * FROM TMP WITH(INDEX = ix_teste)
WHERE Nome = ‘A’
/*
Table ‘TMP’. Scan count 1, logical reads 2076, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Podemos observar que desta vez o SQL leu 2076 páginas de dados para retornar o resultado.
Com isso podemos concluir que o Lookup é MUITO "caro" para sua consulta e o
SQL só irá optar fazer o lookup se a quantidade de linhas que será retornada for
aproximadamente menor que 0,1% do total de sua tabela.
Uma solução para forçar o uso do indice sem perder tanto em performance
seria incluir a coluna Nome2 no indice noncluster,
No SQL 2000
CREATE INDEX ix_Teste on TMP(Nome, Nome2)
No SQL 2005 poderiamos utilizar a clausula INCLUDE
CREATE INDEX ix_Teste on TMP(Nome) INCLUDE(Nome2)
*/
/*
Vamos incluir uma linha na tabela TMP com o valor X e efetuar
o select para retonar apenas o valor X e ver o plano de execução.
*/
INSERT INTO TMP(NOME, Nome2) VALUES(‘X’, NewID())
SELECT * FROM TMP
WHERE Nome = ‘X’
GO
SELECT * FROM TMP
WHERE Nome = ‘A’
GO
/*
Repare que no primeiro select o SQL usou o Indice e no
segundo não usou porque irá retornar muitas linhas
*/
/*
Um outro comportamento interessante é quando o valor do filtro é passado
por uma variável, vimos no select acima que o SQL usou o indice para
o filtro nome = ‘X’, vamos passar o valor ‘X’ para uma variável
e utiliza-la no where.
*/
DECLARE @var VarChar(200)
SET @var = ‘X’
SELECT * FROM TMP
WHERE Nome = @var
/*
Desta vez o SQL não usou o indice e fez o Scan mesmo a consulta retornando
apenas 1 registro, isso acontece porque o SQL não sabe o valor de @var
por este motivo ele opta por fazer o Scan porque ele não sabe se @var irá retornar
1 linha ou 5000.
Existem várias soluções para este problema, a idéia a fazer o SQL compilar
o código com o valor de @var.
Você poderia utilizar a SP_EXECUTESQL
Ou então criar uma procedure e executar a consulta na procedure
Ou então utilizar o Hint RECOMPILE
Veja um exemplo do uso das 3 opções
*/
— SP_EXECUTESQL
DECLARE @SQL NVARCHAR(200)
SET @SQL = ‘SELECT * FROM TMP WHERE Nome = @Var’
EXEC SP_EXECUTESQL @SQL, N’@Var VarChar(200)’, @Var = ‘X’
— Procedure
— Criei a proc utilizando a clausula WITH RECOMPILE
— mas esse assunto fica pra uma 2 Parte do post
CREATE PROC st_Teste @Var VarChar(200)
WITH RECOMPILE AS
SELECT * FROM TMP
WHERE Nome = @var
GO
EXEC st_Teste @Var = ‘A’
— HINT – RECOMPILE
DECLARE @var VarChar(200)
SET @var = ‘X’
SELECT * FROM TMP
WHERE Nome = @var
OPTION(RECOMPILE)
_________________________________________________________________
Fabiano Neves Amorim
Análise – NewCon Enterprise
* fabiano@cnpm.com.br – http://fabianosqlserver.spaces.live.com/
SQL Server 2008 VS 29 de Fevereiro
Eu gosto muito da Microsoft, seus produtos são muito bons e tal mas de vez em quanto(ta bom vai, direto) ela da uns foras que não tem desculpa.
Estou utilizando o SQL Server 2008 CTP6 versão de Fevereiro que foi lançada esses dias, e hoje meu SQL Server parou de Iniciar pois da um erro na inicialização no serviço da engine.
Segue o Log do erro:
2008-02-29 14:19:40.42 Server Error: 17190, Severity: 16, State: 1.
2008-02-29 14:19:40.42 Server FallBack certificate initialization failed with error code: 5.
2008-02-29 14:19:40.56 Server Unable to initialize SSL encryption because a valid certificate could not be found, and it is not possible to create a self-signed certificate.
2008-02-29 14:19:40.60 Server Error: 17182, Severity: 16, State: 1.
2008-02-29 14:19:40.60 Server TDSSNIClient initialization failed with error 0x80092004, status code 0x80. Reason: Unable to initialize SSL support.
2008-02-29 14:19:40.60 Server Error: 17182, Severity: 16, State: 1.
2008-02-29 14:19:40.60 Server TDSSNIClient initialization failed with error 0x80092004, status code 0x1. Reason: Initialization failed with an infrastructure error. Check for previous errors.
2008-02-29 14:19:40.60 Server Error: 17826, Severity: 18, State: 3.
2008-02-29 14:19:40.60 Server Could not start the network library because of an internal error in the network library. To determine the cause, review the errors immediately preceding this one in the error log.
2008-02-29 14:19:40.63 Server Error: 17120, Severity: 16, State: 1.
2008-02-29 14:19:40.63 Server SQL Server could not spawn FRunCM thread. Check the SQL Server error log and the Windows event logs for information about possible related problems.
Depois de pesquisar um pouco na internet descobri que as CTPs do SQL 2008 não funcionam no dia 29 de Fevereiro, como o dia de hoje só ocorre a cada 4 anos recomendaram que não usemos o SQL hoje.
Segue o texto e o link de palavras do Sr. Christian Kleinerman (Microsoft) Group Program Manager SQL Server 2008 que fala um espanhol muito bom por sinal, assisti uma palestra dele falando sobre o SQL 2008 no evento da Solid Quality em SP no ano passado.
“We have recently discovered an issue with SQL Server 2008 CTPs that result in SQL Server 2008 not starting or installing on Feb 29 GMT only. We recommend that you do not run, install or upgrade this CTP on Feb 29 GMT to minimize any impact in your environment. You can install starting on March 1 GMT. If you have an immediate issue that cannot wait until march 1st GMT contact csskat@microsoft.com before taking any further steps.”
Ta bom eu sei que é uma versão para testes e tudo mais só que um erro desse me parece meio que culpa de algum estagiário(porque é sempre culpa deles?) que desenvolveu o código.
Ainda bem que procurei sobre o problema no www.google.com.br e não no www.live.com/?searchonly=true pois vai que hoje as buscas no live não retornem muito bem! Vocês podem achar que estou exagerando mas isso é só porque vocês não tem o SQL 2008 instalado em suas máquinas, Fico pensando o que pode acontecer com no Halloween ou alguma sexta-feira 13 que venha por ai. J
SysProcesses
Hoje fiz um comando SQL para saber quem está gastando mais recursos do banco SQL, o comando é legal porque mostra o SQL que está sendo executado pela sessão.
SELECT
Hostname,
DB_Name(DBID) Banco,
Blocked,
CPU,
Physical_IO,
MemUsage,
(SELECT TEXT FROM ::fn_get_sql(SQL_Handle)) AS Comando_SQL,
Login_time,
Last_Batch,
Open_Tran,
Program_Name
FROM master.dbo.sysprocesses
WHERE spid > 50
ORDER BY Physical_IO desc, cpu DESC
Obs.: Só roda no SQL Server 2005 L
Evento Online de SQL Server
Evento online que aconteceu a alguns dias atrás, tem várias WebCasts de SQL Server falando sobre assuntos variados, vale a pena reservar um tempo para dar uma olhada.
http://events.unisfair.com/index.jsp
_________________________________________________________________
Fabiano Neves Amorim
Análise – NewCon Enterprise
* fabiano@cnpm.com.br – http://fabianosqlserver.spaces.live.com/
( (55 – 14) 3404-3700
Business Intelligence Microsoft
Business Objects vendida pra a SAP por $6,8 bi e a Cognos vendida para a IBM por $5,0 bi, e qual foi o resultado?
Microsoft líder em vendas de Business Intelligence,
http://www.informationweek.com/news/showArticle.jhtml?articleID=206104502&subSection=News
PerfMom e Profiler
Hoje no newsletter da SQLTeam.com recebi o link para um artigo falando sobre a integração entre o PerfMom e o Profiler 2005.
Eu já tinha visto o Javier Loría da Solid Quality abrir um log do Performance Monitor no Profiler do SQL Server 2005, assim como usar o Profiler para capturar os códigos MDXs gerados pelo Analysis Services, quem já usou o OWC e sempre quis ver o MDX que o componente gerava fica ai a dica, voltando ao PerfMom, após ler o artigo e fazer alguns testes na minha máquina cheguei a seguinte conclusão.
DBAs só não fazer o trabalho deles por 3 motivos:
1. Porque não querem.
2. Porque não sabem.
3. Porque gostam de culpar os desenvolvedores pelos problemas de performance J.
Como diria a Delaney, Have Fun!
Status Backup\Restore
Hoje usei um script bem legal que peguei no Blog do MVP SQL Server Junior Galvão.
Ele retorna varias informações sobre um backup ou restore que está acontecendo no banco.
SELECT command,
Dateadd(ms,estimated_completion_time,Getdate()) AS Hora_Estimada_ParaTermino,
estimated_completion_time / 1000 AS Segundos_Para_Termino,
estimated_completion_time / 1000 / 60 Minutos_Para_Termino,
start_time AS Inicio_do_Comando,
percent_complete AS Percentual_Completo
FROM sys.dm_exec_requests
WHERE session_id = <Numero da sessão que está rodando o comando>
Thanks Junior.
__________________________________________________________________
Fabiano Neves Amorim
Análise – NewCon Enterprise
* fabiano@cnpm.com.br – http://fabianosqlserver.spaces.live.com/
( (55 – 14) 3404-3700
Ainda sobre TempDB
Ainda falando sobre o TempDB, hoje li um artigo escrito pelo Paul Randal, vale a pena gastar um tempo para ler.
Achei bem legal ele falando que mesmo quando se trata do mesmo assunto existem várias idéias e opiniões diferentes por parte dos funcionários da Microsoft. Portanto não devemos SEMPRE confiar em tudo que eles dizem ou escrevem.
Fiquei imaginando a cena, o cara querendo convencer o Paul de que o DBCC CHECKDB é isso e não é aquilo, e ele simplesmente respondendo, ‘I wrote that code – I’m afraid you *are* wrong’. (Eu escrevi o código – Temo que você está errado)
Obs.: “I’m afraid” segundo o Concise Oxford Dictionary significa pedir desculpas educadamente, rs senti uma certo sarcasmo na frase.
Segue o link para o “Debate” sobre criar ou não vários arquivos para o TempDB.
http://www.sqlservercentral.com/Forums/Topic448122-361-2.aspx
__________________________________________________________________
Fabiano Neves Amorim
Análise – NewCon Enterprise
* fabiano@cnpm.com.br – http://fabianosqlserver.spaces.live.com/
( (55 – 14) 3404-3700
TempDB
Em relação a performance um problema muito comum é a contenção no TempDB.
O Banco de dados TempDB é utilizado para várias operações, ao invés de escrever aqui quais são estas operações eu vou de CTRL-C + CTRL+V do Books Online que é mais facil.
The tempdb system database is a global resource that is available to all users connected to the instance of SQL Server and is used to hold the following:
· Temporary user objects that are explicitly created, such as: global or local temporary tables, temporary stored procedures, table variables, or cursors.
· Internal objects that are created by the SQL Server Database Engine, for example, work tables to store intermediate results for spools or sorting.
· Row versions that are generated by data modification transactions in a database that uses read-committed using row versioning isolation or snapshot isolation transactions.
· Row versions that are generated by data modification transactions for features, such as: online index operations, Multiple Active Result Sets (MARS), and AFTER triggers.
No site do TechNet tem um excelente artigo falando sobre o TempDB no SQL Server 2005, segue o link Working with tempdb in SQL Server 2005.
No artigo acima podemos observar em “Troubleshooting contention caused by to DML operations” que quando temos problema de performance no TempDB é recomendado criar um arquivo de dados para cada processador existente no servidor e habilitar o trace flag 1118.
Obs.: Neste caso um processador Dual Core é considerado como 2 processadores, ou seja, criar 2 arquivos.
Conforme o artigo Q328551 a mesma recomendação também foi feita pela Microsoft para servidores rodando SQL Server 2000.
No TechEd 2007 participei de uma mesa redonda sobre Otimização de Performance com o SQL Server 2005 – Dicas e Truques e discutimos sobre o TraceFlag 1118, e os profissionais da Microsoft presentes reforçaram o que os artigos acima recomendam.
Lembro do Fabricio Catae Premier Field Engineer dizer o seguinte:
· Por precaução e para não ter futuros problemas nós recomendamos o uso do TraceFlag 1118 e criamos um arquivo do TempDB para cada processador.
Concordo com ele, na duvida se o TempDB está sendo um problema ou não, antes de ter problema habilite o TraceFlag e crie um arquivo para cada processador do servidor.
Antes de continuar a falar sobre isso vamos entender um pouco mais sobre os Extents pois eles tem tudo a ver com o TraceFlag 1118.
No SQL Server existem dois tipos de extents, os extents mistos e extents uniformes.
Assim como o extent uniforme o extent misto tem 64 kb(8 páginas de 8k). A diferença entre eles é que um extent misto pode ter páginas de mais de um objeto diferente. Veja o exemplo abaixo.
Repare que no Extent Misto temos informações de vários objetos, Table2, Index1, Index2 e etc…
O SQL Server usa 2 tipos de controladores para alocação dos extents, o GAM e o SGAM.
· GAM – Para os extents uniformes o SQL irá usar a Global Allocation Map que é responsável por registrar as alocações dos extents uniformes de todos os objetos do banco de dados. O GAM usa um bit para controlar se o extent está livre ou alocado. Se o bit valer 1 o extent está livre se o bit valor 0 o extent está alocado.
· SGAM – Para os extents mistos o SQL irá usar a Shared Global Allocation Map como o próprio nome diz “Shared” ele é responsável por registrar quais são os extents mistos e qual deles tem páginas livres para uso. O SGAM também usa um bit para fazer este controle, se o bit for 1 então o extent é misto e tem pelo menos uma página livre para utilização, se o bit for 0 então o extent não é um extent misto ou então todas as páginas já estão utilizadas.
Vamos imaginar o seguinte cenário, um simples insert um uma nova tabela temporária.
Todo objeto criado no banco de dados é iniciado com um extent misto até que a tabela ocupe 8 páginas de dados, sabendo disso então podemos dizer que o SQL irá alocar um extend misto para gravar minha informação nesta tabela temporária. Para chegar neste extent misto e na página onde minha informação será gravada o DataBase Engine usa a controladora SGAM.
Ok já sei que o SQL aloca extents mistos para novos objetos, mas o que isso tem a ver com o TraceFlag e o TempDB?
Tem a ver que quando habilitamos o TraceFlag 1118 o SQL passará a não mais alocar extents mistos e sim extents uniformes, pulando a etapa que usa a SGAM para achar um extent misto.
Podemos observar que o SQL tem o trabalho de alocar extents mistos e conforme a tabela vai crescendo o SQL tem controlar se no extent atual cabe os dados que estão sendo inseridos ou se ele precisa alocar um novo extent,isso irá acontecer quando a tabela ficar maior que 64 k, para fazer este controle o SQL irá utilizar a SGAM e a PFS(controla o espaço livre dentro de uma página). Ao alocar direto um extend uniforme o SQL não precisa toda hora consultar a SGAM e a PFS para verificar se ele necessita alocar outro extent para inserir os dados.
O Luciano Moreira deu um show de explicação sobre como, quando, onde e porque utilizar o TraceFlag 1118 o artigo pode ser lido em Analisando o trace flag 1118.
Um detalhe importante é que recentemente(não tão recente assim) a Microsoft divulgou um artigo dizendo que ao fazer o que ela diz você pode ter problemas de desempenho. O artigo pode ser lido aqui Q936185,
O MVP Linchi Shea escreveu um artigo no seu blog falando sobre esse problema, Reduce the Contention on tempdb with Trace Flag 1118: To Enable, or Not to Enable?
Até a próxima e stay tuned!
______________________________________________________________
Fabiano Neves Amorim (MCP – MCTS – SQL Server)
Análise – NewCon Enterprise
* fabiano@cnpm.com.br – http://fabianosqlserver.spaces.live.com/
Upgrade SQL Server
Ao migrar de SQL 2000 para SQL 2005 existem algumas considerações importantes no Transact-SQL que necessitam de nossa atenção, por exemplo existem alguns comandos que hoje estão rodando normalmente no SQL 2000 e ao migrar simplesmente não funcionam, vamos a alguns exemplos.
/*
Exemplo de sintaxes que não funcionam ao migrar de SQL 2000 para 2005
*/
use master
if exists(select * from master.dbo.sysdatabases where name = ‘TesteCompatibilidade’)
begin
drop database TesteCompatibilidade
end
create database TesteCompatibilidade
use TesteCompatibilidade
if OBJECT_ID(‘teste’) is not null
begin
drop table teste
end
GO
— Vamos criar uma tabela para teste
create table teste (id int Identity(1,1), nome VarChar(200) default NEWID())
GO
— Vamos incluir 10 linhas na tabela teste
insert into teste values(default)
go 10
create index ix_teste on teste(nome)
GO
— Seta o banco para usar o nivel de compatibilidade do SQL Server 2000(80)
— Consultas que funcionam normalmente no SQL Server 2000
sp_dbcmptlevel TesteCompatibilidade, 80
GO
–Consulta forcando o uso de um indice sem colocar o WITH depois do nome da tabela
select * from teste(index=ix_teste)
GO
— Consulta usando order by baseado em uma coluna, porem o alias ID é do mesmo nome da coluna ID
— repare que o SQL ordenou a tabela pelo campo ID da tabela e não pelo alias ID(campo nome)
select ID, Nome as ID from teste order by ID
GO
–Consulta usando order by passando o Nome da tabela mais o alias da coluna
select ID, Nome as AliasParaNome from teste order by teste.AliasParaNome
GO
— Marca o banco para usar o nivel de compatibilidade do SQL Server 2005
— Os mesmo selects efetuados acima não funcionan no SQL Server 2005
sp_dbcmptlevel TesteCompatibilidade, 90
GO
–Consulta forcando o uso de um indice sem colocar o WITH depois do nome da tabela
select * from teste(index=ix_teste)
GO
— Consulta usando order by baseado em uma coluna, porem o alias ID é do mesmo nome da coluna ID
— repare que o SQL ordenou a tabela pelo campo ID da tabela e não pelo alias ID(campo nome)
select ID, Nome as ID from teste order by ID
GO
–Consulta usando order by passando o Nome da tabela mais o alias da coluna
select ID, Nome as AliasParaNome from teste order by teste.AliasParaNome
A Microsoft disponibiliza um software que ajuda a evitar surpresas como estas, ele se chama “Microsoft SQL Server 2005 Upgrade Advisor” vale a pena baixar o software e rodar ele na sua base para ver as incompatibilidades. Também vale a pena dar uma lida no “SQL Server 2005 Upgrade Technical Reference Guide”.
_________________________________________________________________
Fabiano Neves Amorim (MCP – MCTS – SQL Server)
Análise – NewCon Enterprise
* fabiano@cnpm.com.br – http://fabianosqlserver.spaces.live.com/
( (55 – 14) 3404-3700





