segunda-feira, 20 de julho de 2009

Bom dia Pessoal!
Na semana passada saiu a listagem completa das sessões do TechEd. Vale a pena dar uma olhada no que teremos nesse ano e reservar o seu lugar ao sol. :-)

http://www.teched.com.br/palestras.aspx

Eu estarei de volta aos palcos do TechEd, dessa vez com duas palestras na track de DBP - Plataforma de banco de dados.

Espero nos encontrarmos por lá...

[]s
Luciano Caixeta Moreira - {Luti}
Chief Innovation Officer
Sr. Nimbus Ltda
E-mail: luciano.moreira@srnimbus.com.br

sexta-feira, 17 de julho de 2009

Plano de execução + cache = DBA enganado

Bom dia pessoal, vamos de SQL Server mais uma vez?

Se você preferir ler o PDF deste artigo, pode baixá-lo aqui.




É comum termos que analisar o plano de execução de uma consulta ou procedimento em nosso banco de dados, aí utilizamos como ponto de partida os famosos STATISTICS IO, STATISTICS TIME e EXECUTION PLAN, para nos ajudar na análise, mas cuidado! Existe algo que pode atrapalhar e levá-lo a tirar conclusões erradas, quer ver?
Em primeiro lugar vamos criar o procedimento ListaPedidosData, que lista todos os pedidos a partir de determinada data:

USE Northwind
GO

IF OBJECT_ID('dbo.ListaPedidosData') IS NOT NULL
DROP PROC dbo.ListaPedidosData
GO


CREATE PROC dbo.ListaPedidosData
@odate AS DATETIME
AS
SELECT OrderID, CustomerID, EmployeeID, OrderDate

FROM dbo.Orders
WHERE OrderDate >= @odate
GO


Criado o procedimento, vamos executá-lo com um parâmetro de alta seletividade (isto é, que retorna poucos registros). Analisando o plano de execução podemos notar que o SQL Server sabiamente optou por fazer um index seek seguindo de quatro key lookups, um para cada registro retornado pelo seek (figura 01), com o seguinte custo de IO: Table 'Orders'. Scan count 1, logical reads 10.

EXEC dbo.ListaPedidosData '19980506'
GO



(Figura 01)

Agora vamos tentar chamar o procedimento com um parâmetro de baixa seletividade, que vai retornar todos os pedidos existentes na tabela (830). O que você espera desse plano de execução? Um clustered index scan, certo? Errado...

EXEC dbo.ListaPedidosData '19960101'
GO


O SQL Server também vai fazer um index seek seguindo de um key lookup (figura 01), pois ele está reutilizando um plano que está em cache e este foi definido dessa forma, então é a maneira que a consulta deve ser executada. Analisando o custo de IO temos: Table 'Orders'. Scan count 1, logical reads 1665. Um pouquinho diferente que o primeiro, certo? Esse é um dos preços que pagamos por estar reutilizando um plano da cache, ele pode não ser bom para o parâmetro atual.
Com o intuito de provar que o plano não é bom para essa seletividade, você acaba por tirar a consulta de dentro do procedimento e executá-la externamente, informando o parâmetro com baixa seletividade. O que você verá? Dessa vez acertou, um clustered index scan (figura 02), com custo de IO: Table 'Orders'. Scan count 1, logical reads 22.



(figura 02)

Pareceu bem melhor que o plano utilizado pela SP, não foi? Aí para termos certeza disso, vamos executar o procedimento e a consulta no mesmo batch e analisar o custo relativo de cada um.

EXEC dbo.ListaPedidosData '19960101'
go


SELECT OrderID, CustomerID, EmployeeID, OrderDate

FROM dbo.Orders

WHERE OrderDate >= '19960101'
go


Qual o resultado? O SQL Server mostra para você que a execução do procedimento tem um custo de 41% do batch, enquanto o scan custa os restantes 59%. Isto é, pelo que o SQL Server nos informa, é mais barato executar o primeiro do que o segundo (figura 03).



(Figura 03)


É nessa hora que você, leitor, grita: ooooohhhhhhhhhh! Eu podia apostar um pedaço do dedo que o segundo plano é o melhor e o SQL Server me diz que o custo dele é maior que o primeiro. Macacos me mordam!
Aí você sai olhando informação de IO, tempo de execução (CPU e Elapsed), custo de tudo que pode imaginar e ainda assim tudo aponta para o segundo como o melhor plano, mas o SQL Server insiste em mostrar um custo menor para o primeiro.
O que está acontecendo?
Faça o seguinte teste. Execute a dupla procedimento/consulta trocando o parâmetro do procedimento por uma série de valores aleatórios, conforme trecho de código abaixo:

-- comparando os tempos...
EXEC dbo.ListaPedidosData '20100101'

go

SELECT OrderID, CustomerID, EmployeeID, OrderDate

FROM dbo.Orders

WHERE OrderDate >= '19960101'

go


-- comparando os tempos...

EXEC dbo.ListaPedidosData '19970101'

go

SELECT OrderID, CustomerID, EmployeeID, OrderDate
FROM dbo.Orders

WHERE OrderDate >= '19960101'

go


O que você vai ver? Que TODOS os resultados relativos de custo estão na proporção 41% e 59%! Opa, então têm alguma coisa “errada”, que é o.... PLAN CACHE!

Você concorda que o otimizador de consultas do SQL Server utilizou uma série de estatísticas para gerar o plano que foi colocado em cache? Não iria fazer muito sentido se a cada execução o SQL Server ficasse estimando novos custos de acordo com os parâmetros, afinal o plano será reutilizado de qualquer forma, então porque perder tempo gerando essas informações? O que temos nos exemplos acima é o SQL Serve reutilizando o plano e também os custos estimados para a consulta original, que retornou somente 4 registros!
Faça o teste executando o script abaixo:

-- Execute os dois juntos SELECT OrderID, CustomerID, EmployeeID, OrderDate FROM dbo.Orders WHERE OrderDate >= '19980506' go SELECT OrderID, CustomerID, EmployeeID, OrderDate FROM dbo.Orders WHERE OrderDate >= '19960101' go
O que você viu? Novamente a proporção 41% e 59%. Aqui faz todo o sentido do mundo, pois um o custo do primeiro é realmente menor que o do segundo. Então para verificar a real proporção entre os custos de execução vamos usar uma hint para simular o plano que está em cache (com valores atualizados) e ver o resultado...

-- Simulando o plano em cache, mas agora com valores atualizados...
SELECT OrderID, CustomerID, EmployeeID, OrderDate

FROM dbo.Orders WITH(INDEX(OrderDate))

WHERE OrderDate >= '19960101'

go


SELECT OrderID, CustomerID, EmployeeID, OrderDate
FROM dbo.Orders

WHERE OrderDate >= '19960101'

go


Como resultado, temos a figura 04.



(Figura 04)

Ah, agora sim! O primeiro tem um custo relativo de 92% enquanto o segundo têm um custo relativo de 8% no batch.
Eu escrevi tudo isso para te dizer o que?

CUIDADO COM OS PLANOS EM CACHE QUANDO ESTIVER FAZENDO A ANÁLISE COMPARATIVA DE SUAS CONSULTAS!

Os valores armazenados em cache para um parâmetro diferente do que você está utilizando serão reaproveitados na hora de mostrar o custo relativo, podendo levá-lo a tirar conclusões erradas em seu ambiente. Então durante sua análise é interessante sempre usar um DBCC FREEPROCCACHE para limpar a cache de planos e usar hints para simular planos, facilitando a comparação.

Espero que você tenha gostado...

[]s
Luciano Caixeta Moreira - {Luti}
Chief Innovation Officer
Sr. Nimbus Ltda
E-mail: luciano.moreira@srnimbus.com.br

terça-feira, 7 de julho de 2009

Affinity mask 0x0 ou 0xFFFFFFF?

Bom dia pessoal.

Durante o último treinamento de internals do SQL Server 2008 que ministrei, acabei pensando em um artigo interessante que pode mostrar como a falta de conhecimento do funcionamento do SQL Server pode impactar o seu ambiente. Hoje vamos falar do affinity mask.
Como vocês já sabem, o affinity mask define quais processadores o SQL Server vai utilizar, então se você possui oito processadores lógicos com o affinity mask definido para 85 (01010101 em binário), está indicando para o SQL Server utilizar o primeiro, terceiro, quinto e sétimo processadores. Por padrão o SQL Server configura o affinity mask como zero (“0”), o que indica que ele pode utilizar todos os processadores.
Com base nas afirmações acima, podemos tirar uma conclusão básica para minha máquina, que possui dois processadores (um slot dual-core): affinity mask = 0 é a mesma coisa que affinity mask 3 (11 em binário), pois ambos indicam que todos os processadores serão utilizados, certo?
Bem, infelizmente a afirmação acima não é totalmente verdadeira, o SQL Server vai utilizar os dois núcleos que tenho disponível, mas seu comportamento é diferente. Quer ver?

SQLOS basics

Sabemos que o SQL Server 2005 (e 2008) é NUMA aware (SQL Server 2000 com SP4 também? Hhhuuumm, bem +/-), o que significa que ele identifica os nós NUMA na sua máquina (hard ou soft) e associa um número X de schedulers a cada um dos nós. Sabendo que os schedulers não migram entre nós e os workers (threads ou fibers – aqui indiferente) estão vinculados a somente um scheduler, o SQLOS tenta balancear a carga entre schedulers através do load factor.
Mas e se, por acaso do destino, um scheduler receber alguns workers que estão executando tarefas mais pesadas e demoradas, haverá um desbalanceamento de uso da CPUs dentro do mesmo nó NUMA? A resposta é não, por padrão um scheduler poderá escolher qual CPU vai utilizar para colocar o worker para trabalhar, deixando aberto um grau de dinamismo entre CPU/scheduler, já que o worker está vinculado ao scheduler.
É claro que o SQL Server somente permite que CPUs do mesmo nó sejam utilizadas por um scheduler, evitando que o ganho pela localidade dos dados na memória do nó seja perdido com a alocação de uma CPU em um nó remoto. Quando falamos em paralelismo isso é factível de acontecer (novos workers em nós diferentes), mas aí com o particionamento dos dados pelo paralelismo você pode ganhar (ao invés de perder) usando as características de uma arquitetura NUMA.
O comportamento que citei acima somente pode acontecer quando o affinity mask está definido como zero. Isto é, se você especificar um affinity mask qualquer, mesmo que indique que todos os processadores serão utilizados, o SQLOS vai entender que um scheduler está vinculado a uma CPU, matando esse pequeno grau de dinamismo e somente confiando no load factor do scheduler.

Demonstração

Vamos a um exemplo simples. Minha máquina possui somente um nó (sys.dm_os_nodes) e dois schedulers (sys.dm_os_schedulers) que são utilizados para as requisições que chegam (deixe de lado hidden schedulers e o reservado para o DAC).
Com affinity mask = 0 eu começo a monitorar o contador “% processor time” (ou “% tempo do processador”) para os dois núcleos e vejo que minha máquina está trabalhando com uma média de 20% (cada um), mas quando eu abro uma nova conexão e executo o script abaixo (script 01) vejo que a média de utilização dos dois contadores sobe para uns 70%.

(Script 01)

DECLARE @I BIGINT = 0
DECLARE @S varchar(200)

WHILE @I < 100000000
BEGIN
select @S = @@VERSION
SET @I += 1
END

O que está acontecendo? A requisição (veja em sys.dm_exec_requests) recebeu um worker e está vinculada a um scheduler, mas esse por sua vez utiliza os dois processadores (em quantuns diferentes) para executar a tarefa, resultado em um uso equilibrado dos núcleos. O número resultante para o contador bate bem em cima do esperado, com um aumento de 50% para cada um.
Vamos mudar um pouco o cenário? Altero o affinity mask para três, indicando que o SQL Server também pode usar os dois núcleos disponíveis. Como resultado, temos a figura 01.

exec sp_configure 'affinity mask', 3
reconfigure


Veja que a diferença entre os dois momentos é clara (figura 01), em um primeiro momento a carga de 100% ficou dividida entre os núcleos, enquanto no segundo momento o SQL Server vinculou o scheduler a somente um núcleo, fazendo com que esse fique constantemente em 100% enquanto o outro fica com aproximadamente 20%.



(Figura 01)


Com isso eu demonstro meu ponto, affinity mask = 0 é diferente de affinity mask = 0xffffff (isto é, usando todos os núcleos), pois no segundo caso o SQL Server mantém uma afinidade entre scheduler e CPU.

O que isso significa para sua empresa? Talvez você não sinta um impacto direto dessa configuração, mas podemos supor um cenário potencial onde, mesmo o SQLOS usando o load factor dos schedulers para definir onde uma tarefa será executada, é factível termos tarefas pesadas caindo sobre um mesmo núcleo, demorando mais e usando de forma desigual os recursos de processamento disponíveis.
Se você quer que o SQL Server trabalhe com processadores específicos (affinity mask diferente de zero), mas não deseja que essa afinidade entre CPU/Scheduler seja respeitada, existe luz no fim do túnel, basta habilitar o trace flag 8002. De onde tirei isso? Leia a série inside SQL Server.

Gostou? Viu como um detalhe simples pode causar um eventual impacto, não reproduzível facilmente, no seu ambiente? Então entenda como o SQL Server funciona para não ser surpreendido...

Prefere ler o PDF deste artigo? Pegue no skydrive.







[]s
Luciano Caixeta Moreira - {Luti Nimbus}
Chief Innovation Officer
Sr. Nimbus Serviços em Tecnologia Ltda
E-mail: luciano.moreira@srnimbus.com.br

Um amigo se tornou MVP

Hoje recebi a ligação de um amigo de Brasília, que se tornou MVP (uma excelente notícia). Estou falando do Rogério Moraes de Carvalho.

Eu conheci o Rogério já faz alguns anos, enquanto ministrava treinamentos na Hepta Informática, e quando trabalhei na Microsoft eu trouxe ele para falar em alguns dos encontros que eu promovi em Brasília, além do TechEd 2008, onde chegamos a palestrar juntos.

Fico feliz pelo Rogério, pois é uma pessoa com um reconhecimento incrível aqui em Brasília e um cara com foco em excelência técnica ao invés de puro marketing pessoal. Espero que ele possa contribuir mais e mais com a comunidade, pois é necessário termos bom profissionais produzindo (e não somente reproduzindo) conteúdo de qualidade.

O anúncio saiu aqui: http://mvpbrasil.spaces.live.com/blog/cns!F5F4692D000CDE16!795.entry

[]s
Luciano Caixeta Moreira - {Luti}
Chief Innovation Officer
Sr. Nimbus Serviços em Tecnologia Ltda
E-mail: luciano.moreira@srnimbus.com.br

segunda-feira, 6 de julho de 2009

Segundo encontro do grupo SQLServerDF

Bom dia pessoal.
Depois de uma semana puxada com prova da Microsoft, prova no MBA e treinamento de internals e tuning do SQL Server 2008, nessa semana vamos fazer o segundo encontro do grupo SQLServerDF, PASS chapter do DF. Aqui está o programa...

Data: dia 09 de Julho de 2009, quinta-feira.
Hora: entre 19:00 e 22:00
Local: Auditório do CEFTRU (ao lado do CESPE) - UnB
Agenda:

- Plan cache, Stored Procedures, parametrização e compilação - (2hs) -
Luciano Caixeta Moreira (Luti)
- Pequeno intervalo
- Detabe técnico.

Descrição da sessão: O objetivo da apresentação é detalhar como funciona a cache de planos do SQL Server e o que acontece com sua consulta SQL ou procedimento, quando executada pela aplicação. Quais são as diferenças entre eles, o que é parametrização adhoc e parameter sniffing, quando uma consulta é (re)compilada e quais são os benefícios e eventuais problemas que a sua aplicação pode enfrentar.
Durante a sessão quebraremos alguns mitos, mostraremos que é importante sim o desenvolvedor conhecer um pouco (ou muito!) do SQL Server e esperamos preparar o pessoal para futuras discussões, como por exemplo, se vale a pena utilizarmos recursos do BD ou trabalharmos com uma tecnologia de mapeamento O/R.

Animou? Mande um e-mail para luciano.moreira@srnimbus.com.br.
Essa sessão é uma versão mais extensa (com mais demos e conteúdo) de uma apresentação que eu fiz no passado para o MSDN, e sinceramente, acho que vale cada minuto.

Se você não é de Brasília, mas quer participar das discussões do grupo e colaborar com o SQLServerDF, pode se juntar a nós através do grupo: http://groups.google.com/group/sqlserverdf, criado por outro membro do time que coordena o SQLServerDF, com o intuito de melhorarmos nossa comunicação.

Para quem não sabe chegar ao CEFTRU, segue um mapa que o pessoal de lá me passou: http://maps.google.com.br/maps?f=d&source=s_d&saddr=Central+%40-15.793379,-47.882592&daddr=Unb&geocode=FR0DD_8doF4l_Q%3BFVRTD_8dN54l_Q&hl=pt-BR&mra=pr&sll=-15.772782,-47.867496&sspn=0.010531,0.01929&ie=UTF8&t=h&z=16

[]s
Luciano Caixeta Moreira - {Luti}
Chief Innovation Officer
Sr. Nimbus Serviços em Tecnologia Ltda
E-mail: luciano.moreira@srnimbus.com.br