quinta-feira, 23 de dezembro de 2010

PASS Summit 2011– Quem vai?

Oi pessoal, como estão as coisas?

Depois de um período longe do blog e da vida social na internet (em breve colocarei um post sobre o assunto) eu estou voltando com uma rápida convocação para os SQLGeeks espalhados por aí. Como acontece todo ano, será realizado o PASS Summit em Seattle, maior evento de SQL Server do planeta, na semana do dia 10 de Outubro de 2011.

No passado eu já falei do evento neste blog (http://luticm.blogspot.com/2010/02/pass-summit-2010-quem-vai.html) e acho que é uma experiência memorável para profissionais que trabalham com SQL Server.

Infelizmente o meu calendário não possibilitou minha ida em 2010, mas 2011 já está planejado e inclusive estou discutindo com algumas pessoas a participação no evento, então se você é um dos interessados, não deixe de se juntar a nós e me envie um e-mail (luciano.moreira@srnimbus.com.br).

Se você fizer o cadastro até o dia 15/01/2010 o evento sai por US$ 995,00 e neste momento não existe desconto para grupos (sim, já conversei com o Shannon), então você pode fazer sua inscrição logo ou esperar até o anúncio dos pacotes em grupo (minha expectativa é por um preço semelhante ao que está em vigor hoje) e, junto com outros interessados, podemos tentar conseguir esse desconto.

Aqui está o site do evento, ainda sem maiores informações: PASS Summit 2011

[]s

Luciano Caixeta Moreira - {Luti}
Sr. Nimbus Serviços em Tecnologia Ltda
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm

quinta-feira, 4 de novembro de 2010

Instalando o SQL Server 2008 e problema com Setup.rll

Estava instalando o SQL Server 2008 em uma VM que já possui o SQL Server 2008 R2 instalado, quando me deparei com o seguinte erro "SQL Server 2008R2\Resouces\1033\Setup.rll is either not designed to run on Windows or it contains an error tryinstalling the program again using the original installation media". Clicando em OK aparecia outra mensagem: "The loading and initialization of setup.rll failed with error code: 0800700C1".
Nada bom, pesquisei rapidinho na Internet mas nada me ajudou, então resolvi dar uma olhada no que temos e pensar um pouco.


Fui até o diretório informado "C:\Program Files\Microsoft SQL Server\100\Setup Bootstrap\Release\Resources\1033" e tentei abrir o rll, mas como não é um XML não nos ajuda. Como já instalei o R2 nessa máquina, suspeitei que o arquivo poderia ser de outra versão do SQL Server (já que o R2 tem como versão 100.50), então se procurarmos na mídia de instalação encontraremos um "setup.rll".

Data do arquivo no diretório: 19/07/2009
Data do arquivo na mídia de instalação: 10/07/2008

Bom, muito bom.
Troquei o nome do arquivo mais recente para setup_new.rll e copiei o arquivo da mídia de instalação para o diretório. Executei o setup e tudo funcionou corretamente.

Dica boba, mas espero que ajude alguma alma.

[]s
Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm
www.srnimbus.com.br

sábado, 30 de outubro de 2010

Sessão: Otimizando carga de dados com o SSIS e SQL Server

Bom dia pessoal.
Devido ao nascimento do meu filho eu não consegui me acertar ao novo ritmo e participar do WOTD (http://www.online.techday.net.br/), um belo evento que aconteceu hoje.

Minha sessão seria: "Otimizando carga de dados com o SSIS e SQL Server". Então para não deixar na mão os que haviam se cadastrado e cumprir o acordado, eu vou marcar um novo horário em novembro e apresentar o meu tema. Será através de uma sessão de live meeting que abrirei (e provavelmente vou passar da hora regulamentar! hahaha).

Fique ligado neste blog e no twitter, pois farei o anúncio em breve.
Me desculpem qualquer transtorno.

[]s
Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm
http://www.srnimbus.com.br/

quarta-feira, 27 de outubro de 2010

Bernardo

Oi. Não é do meu feitio, mas segue um post pessoal.


Dia 25/10/2010 15:19 nasceu meu primeiro filho, o Bernardo.

Todos comentam da emoção de ser pai, mas não adianta falar, é algo indescritível. Nada chega ao pés de você ver seu filho nascer, segurá-lo no colo e sentir que ele está seguro com você.


Sempre desejamos um mundo melhor, e nesse momento você quer se empenhar mais, ser uma pessoa cada vez melhor, tudo para garantir que seu filho ficará seguro e sempre feliz.

Segue uma foto do meu pequeno, algumas horas depois de nascer.


Não vou esticar aqui e escrever um post muito babão, mas espero que todos possam viver a emoção de ser pai um dia.
Vamos aos poucos construir um mundo melhor para todos nós...
 
[]s
Luciano Caixeta Moreira - {Luti}

sexta-feira, 22 de outubro de 2010

Backups de dados vs. Cache de dados


Quer uma formação decente (cansei de brigar com o Blogger vs Live Writer, Word 2010, etc)? Baixe o PDF. :-)



Será que o SQL Server utiliza as páginas que estão em cache para acelerar a realização do backup?

É uma pergunta que já me fizeram algumas vezes, então resolvi colocar um script bem simples para mostrar qual é a resposta à pergunta.
Por simplicidade eu vou colocando os scripts com comentários, então você pode ir acompanhando o que estou fazendo. Em primeiro lugar que tal criarmos um banco para nosso teste e tirarmos um backup para padronizar a brincadeira.

USE
Master

go

-- DROP Database BackupCache

-- Vamos criar uma estrutura simples para brincar...
CREATE
DATABASE BackupCache

go

USE BackupCache
go

CREATE
TABLE TabelaDados

(Codigo INT
IDENTITY(1,1) NOT NULL,

Texto VARCHAR(8000) NOT NULL)
GO

INSERT
INTO TabelaDados
(Texto) VALUES (REPLICATE('Sr. Nimbus', 1000))

go 1000

-- Cria um primeiro backup para deixar o exemplo padronizado
BACKUP
DATABASE BackupCache

TO DISK = 'C:\Temp\BackupCache.bkp'
WITH INIT, Format
go
Como podemos analisar se os dados estão sendo lidos do arquivo físico? Claro, sys.dm_io_virtual_file_stats!
-- Agora vamos a análise
-- Primeiro criamos uma tabela para armazenar filestats...

-- DROP TABLE FileStats

select *, SYSDATETIME() as Hora
INTO FileStats
from sys.dm_io_virtual_file_stats(null,
null)
where 1=2

SELECT *
FROM FileStats


-- Qual nosso backup id?
select DB_ID()
-- = 46


-- O arquivo que me interessa mais é o 1 = MDF
SELECT *, SYSDATETIME()
from sys.dm_io_virtual_file_stats(46, null)
Se o backup estiver lendo os dados diretamente do disco vamos ver a quantidade de bytes lidos (num_of_bytes_read) aumentando, então para esse artigo eu estou me preocupando mais com o MDF. Primeiro vamos fazer um teste limpando primeiro a cache de dados...

-- Em um primeiro momento, vamos fazer um backup com a cache fria
DBCC DROPCLEANBUFFERS


INSERT INTO FileStats
SELECT *, SYSDATETIME()
from sys.dm_io_virtual_file_stats(46, null)

-- 1189 pages
BACKUP DATABASE BackupCache
TO DISK = 'C:\Temp\BackupCache.bkp'
WITH INIT, Format

/*
Processed 1184 pages for database 'BackupCache', file 'BackupCache' on file 1.
Processed 5 pages for database 'BackupCache', file 'BackupCache_log' on file 1.
BACKUP DATABASE successfully processed 1189 pages in 1.216 seconds (7.633 MB/sec).
*/

INSERT INTO FileStats
SELECT *, SYSDATETIME()
from sys.dm_io_virtual_file_stats(46, null)
go


Analisando a saída da DMV conseguimos ver que foram lidos 9953280 bytes, exatamente 1215 páginas. Um pouco a mais que o número de páginas processadas, talvez páginas lidas duas vezes ou algo que não vai para backup. O importante aqui é que as páginas foram lidas diretamente do arquivo físico.
Agora vamos testar com as páginas de dados em cache.

-- E se as páginas de dados já estiverem em cache?
-- Notem que o backup NÃO colocou as páginas em cache
select
*

from
sys.dm_os_buffer_descriptors

where database_id = 46


select
*

from TabelaDados


-- 1067 páginas em cache (o número pode variar)
select
*

from
sys.dm_os_buffer_descriptors

where database_id = 46


-- Agora vamos ao backup...
INSERT
INTO FileStats

SELECT
*,
SYSDATETIME()

from
sys.dm_io_virtual_file_stats(46,
null)



-- 1189 pages
BACKUP
DATABASE BackupCache

TO
DISK
=
'C:\Temp\BackupCache.bkp'

WITH
INIT, Format



/*
Processed 1184 pages for database 'BackupCache', file 'BackupCache' on file 1.
Processed 2 pages for database 'BackupCache', file 'BackupCache_log' on file 1.
BACKUP DATABASE successfully processed 1186 pages in 1.442 seconds (6.422 MB/sec).
*/


INSERT
INTO FileStats

SELECT
*,
SYSDATETIME()

from
sys.dm_io_virtual_file_stats(46,
null)

go

Fazendo novamente o cálculo com base nas leituras físicas que foram feitas...

-- Analisando as duas últimas entradas do arquivo de dados (BytesRead)
PRINT (38912000 - 29212672)
= PRINT
9699328 / 8192 = 1184 páginas



-- Foram lidas 1184 páginas do arquivo físico.
-- Alguma página que estava em memória foi utilizada? NÃO!


Então aqui já temos nossa resposta, mas só para deixar a coisa mais divertida, e se tivermos páginas sujas em cache?! Primeiro deixamos as páginas sujas (notem que já foi feito o flush de muitas algumas páginas alteradas, o que já forçou escritas no arquivo e isso pode ser confirmado pelo file stats) e depois realizamos o backup.

-- E com dirty pages? Veja coluna is_modified
select
*

from
sys.dm_os_buffer_descriptors

where database_id = 46
    AND is_modified = 1


UPDATE TabelaDados
    SET Texto =
REPLICATE('Sr! Nimbus', 1000)



-- Flush de dirty pages foi feito e sobraram N páginas sujas... (313 no meu caso)
select
*

from
sys.dm_os_buffer_descriptors

where database_id = 46
    AND is_modified = 1
go


-- Agora vamos ao backup...
INSERT
INTO FileStats

SELECT
*,
SYSDATETIME()

from
sys.dm_io_virtual_file_stats(46,
null)



BACKUP
DATABASE BackupCache

TO
DISK
=
'C:\Temp\BackupCache.bkp'

WITH
INIT, Format



/*
Processed 1184 pages for database 'BackupCache', file 'BackupCache' on file 1.
Processed 2 pages for database 'BackupCache', file 'BackupCache_log' on file 1.
BACKUP DATABASE successfully processed 1186 pages in 1.322 seconds (7.005 MB/sec).
*/


INSERT
INTO FileStats

SELECT
*,
SYSDATETIME()

from
sys.dm_io_virtual_file_stats(46,
null)

go

select

*
from FileStats

-- Analisando as duas últimas entradas do arquivo de dados (BytesRead)
PRINT (48611328 - 38912000)
= PRINT
9699328 / 8192 = 1184 páginas



Analisando os file stats, vemos novamente que o SQL Server leu as páginas do disco! Hhuumm, e as páginas sujas que estavam em memória? Sumiram! Of course my horse, o backup disparou um checkpoint que fez um flush das páginas sujas! Duvida? Veja o log de transação.

-- Aonde foram parar mas minhas páginas sujas??? Somente 4 páginas?
-- Sim, lembra que o backup executa um checkpoint? Flush foi feito...
select
*

from
sys.dm_os_buffer_descriptors

where database_id = 46
    AND is_modified = 1
    
/*
72057594039894016    DATA_PAGE
281474978283520    DATA_PAGE
6488064    FILEHEADER_PAGE
6488064    DIFF_MAP_PAGE
*/
-- O que, não acredita?
SELECT
*
FROM
fn_dblog(null,
null)



/*
Viu o BEGIN_CKPT e END_CKPT?


00000084:00000131:003a    LOP_BEGIN_CKPT    LCX_NULL
00000084:00000148:0001    LOP_END_CKPT    LCX_NULL
00000084:00000149:0001    LOP_BEGIN_XACT    LCX_NULL
00000084:00000149:0002    LOP_MODIFY_COLUMNS    LCX_CLUSTERED
00000084:00000149:0003    LOP_PREP_XACT    LCX_NULL
00000084:0000014a:0001    LOP_COMMIT_XACT    LCX_NULL
00000084:0000014b:0001    LOP_FILE_HDR_MODIFY    LCX_FILE_HEADER
00000084:0000014b:0002    LOP_MODIFY_ROW    LCX_BOOT_PAGE_CKPT
00000084:0000014b:0003    LOP_MODIFY_ROW    LCX_BOOT_PAGE_CKPT
00000084:00000150:0001    LOP_BEGIN_XACT    LCX_NULL
00000084:00000150:0002    LOP_SET_BITS    LCX_DIFF_MAP
00000084:00000150:0003    LOP_INSERT_ROWS    LCX_HEAP
00000084:00000150:0004    LOP_INSERT_ROWS    LCX_HEAP
00000084:00000150:0005    LOP_COMMIT_XACT    LCX_NULL

Ô curioso, se você olhar no log as páginas que sofreram alterações, vai ver 4 páginas.
Nossa, que coincidência com o buffer descriptors! HAHAHAHA. Lindo.
*/

Resumindo a brincadeira, os backups do SQL Server vão ler diretamente as páginas que estão em disco e NÃO vão usar as páginas que estão em cache, independentemente de você tiver 64GB de dados já em memória. Então você deve tomar cuidado com os backups de dados durante o dia, pois eles podem sim impactar diretamente seu subsistema de discos.
Espero que você tenha se divertido.
[]s
Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm
http://www.srnimbus.com.br/

quarta-feira, 13 de outubro de 2010

24 horas de SQL Server = 24H PASS LATAM

Bom dia pessoal.
Na próxima semana eu estou vou fazer mais uma apresentação virtual para todos aqueles que gostam de SQL Server e SSIS. E o melhor, não sou só eu quem vai falar, na verdade outros 23 feras de SQL Server também estarão falando sobre SQL Server 2008 (e R2).

O evento é uma organização do PASS (Professional Association for SQL Server - http://www.sqlpass.org/) na América Latina. Os detalhes do evento e toda as sessões você pode conferir aqui: http://www.sqlpass-latam.org/24horas.aspx.

Minha apresentação

Título: Padrões de pacotes no SSIS
Descrição: Não falamos muito de padrões de projetos e padrões de arquitetura? Por que não falar sobre padrões de pacotes no SSIS? Nessa sessão eu quero falar um pouco sobre a possibilidade de se reutilizar estruturas comuns para pacotes no SSIS e propor alguns padrões que eu tenho encontrado enquanto estou trabalhando com o Integration Services. Claro que não sou um Fowler ou Gamma, muito longe disso, mas se conseguir com a sessão incentivar alguma coisa em torno do assunto, será muito interessante.

Gostou da proposta? Então pare uma horinha e participe da sessão!

NOTA: As sessões estão marcadas para um horário e nosso fuso está três horas a frente deles, então quando a sessão que se inicia 7:00h para Brasília será 10:00h. Com isso minha sessão será 15:00h amanhã.

[]s
Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm
www.srnimbus.com.br

quinta-feira, 7 de outubro de 2010

Oportunidade Nimbus: Desenvolvedor de soluções na plataforma Microsoft .NET

A Sr. Nimbus é uma empresa de pequeno porte especializada em consultoria e treinamento na plataforma de desenvolvimento Microsoft. Um de nossos objetivos é auxiliar nossos clientes a atingirem o seu potencial máximo no uso das ferramentas desta plataforma. Para isto, temos que ter uma equipe bem preparada e experiente. E um de nossos desejos é conseguir este tipo de profissional através de nossas “categorias de base”: realizar a preparação e orientação de profissionais promissores, mas ainda sem grande contato com o mercado.

Nosso processo de trabalho emprega as seguintes disciplinas: 

Estamos com uma vaga aberta na área de desenvolvimento de soluções. Os pré-requisitos da vaga são:
  • Experiência em desenvolvimento C#.
  • Experiência em desenvolvimento ASP.NET.

Nosso objetivo para este profissional é a formação de um consultor especializado nas principais tecnologias das plataformas de desenvolmimento Microsoft: .NET Framework, Windows Azure e SharePoint. A formação envolverá cursos, self-study, shadowing de nossos consultores sêniores nos seus atendimentos, além de, é claro, muita “mão-na-massa”.

Se você se interessou, mande seu currículo para gilberto.uchoa@srnimbus.com.br. 
Abraços
 
Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm
www.srnimbus.com.br

sexta-feira, 1 de outubro de 2010

SQL Server 2008 R2 install - Could Not Find Database Engine Startup Handle

Hoje eu resolvi montar uma nova VM e comecei pela instalação do SQL Server 2008 R2 em uma máquina virtual. Estou rodando o VirtualPC 2007 SP1 no Win 7 x64 e a máquina virtual está rodando o Windows Server 2008.

Seguindo normalmente a instalação que já fiz 48934932 vezes, tudo ia bem até que “Could Not Find Database Engine Startup Handle”. As ferramentas eram instaladas, mas a engine relacional nada. Fiz alguns testei, mudei algumas coisas e nada! Sempre que tentava instalar o SQL Server ele parava no mesmo ponto.

Pesquisando um pouco encontrei este artigo que me foi útil: http://www.msbicentral.com/Resources/Articles/tabid/88/articleType/ArticleView/articleId/80/FLASH-SQL-Server-2008-R2-Error-Could-Not-Find-Database-Engine-Startup-Handle.aspx.

Para não queimar uma mídia peguei o ISO, fiz um extract com o WinRar e copiei os arquivos para dentro da VM. Reiniciei a instalação e tudo funcionou perfeitamente.

Espero que possa ajudar.

[]s

Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm
www.srnimbus.com.br

sexta-feira, 24 de setembro de 2010

Internet Explorer 9 e a zica com o firewall

Não gosto de postar sobre coisas sem solução, mas gostaria de ouvir se mais alguém teve um problema semelhante ao meu. Pesquisei e nada encontrei.
Ontem eu instalei o IE9 e comecei a brincar com o bichinho, de cara descobri que existe um problema de compatibilidade com o Dynamic CRM 4.0 e precisei usar um sandbox do IE8 (www.spoon.net) para resolver meu problema (e não, modo de compatibilidade não é suficiente!), inclusive em um rollup do CRM temos algumas coisas voltadas para o IE9 (http://blogs.msdn.com/b/crm/archive/2010/09/23/update-rollup-13-for-microsoft-dynamics-crm-4-0.aspx).

Até aí tudo bem, mas hoje cedo encontrei um problema em que eu não conseguia acessar nenhuma máquina por TS ou acessar um SQL Server remoto. Se tentasse de outra estação tudo funcionava corretamente, mas nada da minha máquina. Claramente um problema aqui, verificamos a rota de uma requisição e chegamos até um log do firewall Aker, que mostrava:


Eita, flags TCP do pacote são inválidos?! Pesquisei e nada achei, até que descobri outro caso semelhante com um amigo e ele havia resolvido o problema removendo o IE9. Cético, removi o IE9 e... tudo funcionou! Que droga.

Não sei detalhes sobre o motivo do acontecido, mas pesquisei e nada encontrei. Seria algo do IE9? Do firewall Aker? Se você souber de algo ou um workaround, favor me avisar.

[]s
Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm
www.srnimbus.com.br

segunda-feira, 20 de setembro de 2010

Sim, é um deadlock

Se você prefere ler o artigo em PDF, baixe aqui.


Revisando os posts da última semana eu encontrei um post interessante do nosso amigo Alexandre Lopes, que mostra um exemplo bem legal de deadlock. O post na íntegra está aqui: http://sqlserverday.com.br/alopes/?p=924 e aconselho lê-lo antes de continuar com
este post. Leu tudo? Então vamos a uma análise mais detalhada.


Monitorando com o profiler os eventos lock:acquired e lock:released, execute no banco de dados AdventureWorks a consulta “SELECT * FROM Sales.CurrencyRate” e veremos (Figura 01) que o lock manager optou por pegar um bloqueio de página, então temos um IS no objeto e, durante a leitura dos registros, o SQL Server vai pegando e liberando os bloqueios compartilhados na medida em que os registros das páginas são lidos.
clip_image002
(Figura 01)

Agora vamos ao deadlock, com base no script abaixo:

(Script 01)

USE AdventureWorks
go

select @@TRANCOUNT

-- Conexão 01
BEGIN TRAN

UPDATE Sales.CurrencyRate SET AverageRate = 2 WHERE CurrencyRateID =1
SELECT * FROM Sales.CurrencyRate

-- Script 02
BEGIN TRAN

UPDATE Sales.CurrencyRate SET AverageRate = 2 WHERE CurrencyRateID = 1

Quando iniciamos a transação da conexão 01 e executamos o primeiro update, veremos o SQL Server pegando um lock exclusivo no registro e IX na página e tabela (Figura 02).

clip_image004
(Figura 02)

Se a partir de outra conexão executarmos o mesmo comando de update, o que é de se esperar? Que o SQL Server requisite os mesmos recursos e a transação fique bloqueada pelo registro exclusivo colocado sobre a chave. Vejamos a saída do SP_LOCK para análise na figura 03.

clip_image006
(Figura 03)

Vemos claramente que a conexão de SPID 55 ficou bloqueada (WAIT) esperando que a conexão 52 libere o bloqueio exclusivo sobre o registro que está sendo atualizado. Outro ponto que eu quero chamar a atenção é para o bloqueio IX da página 1040, que foi concedido para a transação de SPID 55.

Se buscarmos no BOL o tópico “Lock Compatibility” veremos que IX são compatíveis com IX e até existe uma nota sobre isso: “An intent exclusive (IX) lock is compatible with an IX lock mode because IX means the intention is to update only some of the rows rather than all of them. Other transactions that attempt to read or update some of the rows are also permitted as long as they are not the same rows being updated by other transactions”.


Porém se analisarmos a tabela de compatibilidade, veremos que o bloqueio compartilhado (S) não é compatível com o IX, pois eu não poderia ler uma página em que algum registro estivesse sendo alterado, seria uma leitura suja não compatível com o nível de isolamento read committed. Acho que vocês já devem ter percebido onde eu quero chegar...


Se nesse momento nós voltarmos para a primeira transação que está com o bloqueio exclusivo no registro e executarmos o SELECT para buscar todos os registros, o que veremos? Sim senhor, um deadlock. E os recursos envolvidos são detalhados na figura 04.

clip_image008
(Figura 04)

Analisando a saída acima, vemos as sessões 52 e 55 se bloqueando, então estamos em uma situação de deadlock, onde o SQL Server deve escolher uma vítima. Os recursos envolvidos são o registro bloqueado pelo lock exclusivo e a página 1040, onde a transação do SPID 52 fica bloqueada com o status CNVT (Convert). Pela documentação do SQL Server: “CNVRT: The lock is being converted from another mode, but the conversion is blocked by another process holding a lock with a conflicting mode”.

Então o deadlock aconteceu porque estamos trabalhando em níveis de granularidade diferentes, um na página e outro no registro! Se o que eu acabei de afirmar for verdade, esse tipo de deadlock pode ser resolvido com uma hint no SELECT, onde indicamos que o SQL Server deve pegar bloqueios de registros, e não de página: “SELECT * FROM Sales.CurrencyRate (ROWLOCK)”. Faça o teste e verá que realmente dessa forma o deadlock não irá acontecer.

Conclusão

O comportamento do SQL Server está correto e é realmente um deadlock, então vou discordar do Alexandre em relação à qualidade do produto e que não existe um deadlock, mas sim tentar pensar como um program manager do produto. Qual é o maior ganho para o SQL Server? Permitir bloqueios de registro e utilizar o IX para indicar que existe algum recurso com menor granularidade sendo bloqueado (acelerando a análise de compatibilidade dos bloqueios) ou evitar esse comportamento impedindo bloqueios de registro ou análises custosas de compatibilidade de bloqueios em diferentes níveis (imagine bilhões de registros, um bloqueio exclusivo em um registro e outra transação pedindo um bloqueio compartilhado na tabela). Eu também ficaria com o row lock.

No fim eu gostei bastante de fazer essa pequena análise, pois é um excelente exemplo (e não muito comum) de um deadlock que o Alexandre trouxe para nós, possibilitando mostrar para vocês um pouco mais do SQL Server e seu funcionamento.


Abraços e até um próximo artigo.

[]s
Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br www.twitter.com/luticm www.srnimbus.com.br

quarta-feira, 15 de setembro de 2010

Non-SARG que nada! Enganei o SQL Server...

Bom dia pessoal.

Prefere ler em PDF?




Ontem eu citei em uma palestra do Teched 2010 que um index seek exibido no plano de execução pode esconder na verdade um range scan ou até um full scan, então vou pegar um gancho de uma pergunta que apareceu recentemente em um treinamento que estava ministrando.

Explicando sobre non-search arguments eu demonstrei a consulta 01, que busca as vendas de um determinado mês. Porém sendo um non-sarg, o SQL Server utiliza o índice OrderDate (NCL) mas não faz um seek, e sim um NonClustered Index Scan, varrendo 4 páginas (o índice é muito pequeno).


Para resolver o problema, vamos alterar a consulta removendo o non-sarg, conforme a consulta 02, fazendo com que o SQL Server faça um seek (lendo duas páginas - raiz e folha) ao invés de um scan.


-- Consulta 01
SELECT orderdate, OrderID FROM Orders WHERE MONTH(OrderDate) = 07 and YEAR(OrderDate) = 1996


-- Consulta 02
SELECT OrderDate, OrderID FROM Orders WHERE OrderDate between '19960701' and '19960731 23:59:59.997'

Nesse momento um DBA fez uma observação muito curiosa: “eu posso enganar o SQL Server!” E para isso basta executar a consulta 03.

-- Consulta 03
SELECT OrderDate, OrderId FROM Orders WHERE MONTH(OrderDate) = 07 and YEAR(OrderDate) = 1996 and OrderDate > 0


Prontinho, se você olhar o plano gerado pelo SQL Server (figura 01) verá que ele está fazendo um index seek, ao invés do index scan gerado originalmente pelo non-sarg. Resolvido? No no no meu caro, na verdade não estamos enganando o SQL Server, infelizmente estamos sendo enganados.



(Figura 01)

Se olharmos com calma, o seek predicate é somente o “orderdate > 1990-01-01”, isto é, um belo scan no nosso índice não-cluster e enquanto o SQL Server está fazendo esse “seek”, ele vai tentando aplicar o predicado com MONTH e YEAR, que é o nosso non-sarg.


Putz, como eu sei que o SQL Server está fazendo um index scan? Dê uma olhada no STATISTICS IO e você verá o seguinte: “Table 'Orders'. Scan count 1, logical reads 4”. Hhhuummm, scan count = 1 (o SQL Server está fazendo um scan!) e logical reads = 4 é o mesmo que vimos durante a execução da consulta 01. Houve então alguma diferença efetiva no plano de execução? NÃO! Se você executar lado a lado a consulta 01 e a 03, verá um custo relativo de 50% para cada.


O que me incentivou a finalmente escrever esse post? Hoje cedo em vi que o time de CSS postou um artigo bem legal, chamado “SCAN COUNT meaning in SET STATISTICS IO output”, que explica um pouco sobre o scan count e serve de base para esse post, em que o “falso index seek” que é um full scan ou um range scan.

Espero que seja útil, e não deixe esses detalhes do SQL Server te enganar, ok? :-)
Abraços e até um próximo artigo.


[]s
Luciano Caixeta Moreira - {Luti}

terça-feira, 14 de setembro de 2010

TechEd Brasil 2010 - [DBP402] Material da apresentação

Oi pessoal.

Fechei hoje minha participação no TechEd Brasil 2010 como apresentador, foram 3 sessões em menos de 24 horas (todas entregues em 21:45h para ser preciso) e agora é hora de relaxar e aproveitar o resto do evento.

Como prometido, vou postar aqui os slides da minha apresentação e também os projetos/scripts que utilizei ao longo das sessões. E vou começar de trás para frente, para matar uma agonia pessoal.

Pegue aqui o PDF e script da demo.



Aproveitando, uma pequena demonstração e uma errata...
Primeiro a errata, que está me matando.

Durante a apresentação me perguntaram se o sp_recompile têm o mesmo efeito que o WITH RECOMPILE. Respondi que sim e continuei a palestra, mas meu processamento em background ficou me cutucando: "acho que você falou merda". E realmente falei besteira, o comportamento é diferente e o sp_recompile resolve nosso problema para tirar a procedure do plan cache.

Dito isso e mais aliviado, vamos a outro item que não tive tempo de detalhar, quando estava falando em auto parametrização eu troquei o script (já corrigi neste post) e acabei executando uma consulta que é auto parametrizada.

SELECT OrderID, CustomerID, EmployeeID, OrderDate FROM dbo.Orders WHERE OrderID >= 11071

Essa consulta é auto parametrizada pois independente do que você passe para o SQL Server, ele irá fazer um range scan, apesar dele somente mostrar um index seek no plano (não vemos o detalhe do range scan). Então se vocês olharem o STATISTICS IO e os planos das consultas que retornam os 830 registros, verão que o SCAN completo e o index seek, ambos farão uma leitura de 22 páginas.


dbcc freeproccache
SELECT OrderID, CustomerID, EmployeeID, OrderDate FROM dbo.Orders WHERE OrderID >= 11071

SELECT * FROM dbo.Orders

SELECT OrderID, CustomerID, EmployeeID, OrderDate FROM dbo.Orders WHERE OrderID >= 10248

Também deixo aqui uma brincadeira, para aqueles que querem navegar pela estrutura dos índices e acompanhar o range scan. :-)


SELECT * FROM sys.sysindexes where id = OBJECT_ID('Orders')
-- ROOT: 0x350100000100
-- First: 0x1B0100000100


SELECT DB_ID()
DBCC TRACEON(3604)
DBCC PAGE (6, 1, 283, 3)

-- 283
-- 310
-- 348
DBCC IND (Northwind, Orders, 1)

-- Filtragem dos 8 registros
DBCC TRACEON(3604)
DBCC PAGE (6, 1, 348, 3)

Até daqui a pouco.
[]s
 
Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm
www.srnimbus.com.br

quarta-feira, 1 de setembro de 2010

Aqui o SQL Server vai viver feliz

Olha que coisa linda para um geek...


E isso que vocês não viram a storage SSD...
Aqui o SQL Server 2008 R2 vai viver feliz, feliz.
[]s
Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm
www.srnimbus.com.br

quarta-feira, 4 de agosto de 2010

Mitos sobre Stored Procedures, uma análise

Quer ler o PDF deste post, baixe aqui.

Recentemente o Giggio escreveu um post intitulado “Mitos sobre Stored Procedures” e até me mandou um tweet dizendo “Alguma coisa a completar sobre o que escrevi?!”. Somente hoje consegui parar e fazer uma análise crítica do post e das respostas, então resolvi colocar tudo em forma de post.
Antes de continuar escrevendo, gostaria de ressaltar meu posicionamento sobre o assunto e exemplificar com o Entity Framework, que é o ORM que eu conheço um pouco. 
  1. Sou a favor de frameworks ORMs. Isso mesmo, um SQLGeek falando isso! Gosto da idéia de abstrair a complexidade de bancos relacionais através de um modelo de entidades e permitir que o desenvolvedor possa utilizar o LINQ to Entities (por exemplo), que será traduzido para um T-SQL ou PL-SQL. 
  2. Não acho que ORM é a solução para todos os problemas. Vejo ORMs principalmente para aquelas aplicações (ou parte delas) que fazem manipulações simples de entidades com pequenas massas de dados, CUD ou consultas com um ranking, agrupamentos e outras operações set-based simples. Isto é, para operações data intensive (ex.: fechamento de um processamento diário ou cálculo de premiação mensal) você não vai querer um tráfego gigantesco de dados entre o banco e sua aplicação, só para manter a regra de negócio em um só ponto. 
  3. Programar regras de negócio em C# é mais fácil que em T-SQL. Senhores, T-SQL é uma linguagem restrita para escrita regras de negócio, prefiro 1000 vezes escrever minha lógica em C#. Exemplo bobo, faça um try/catch no T-SQL que acabe em um rethrow da exceção... Eu já tenho a minha proc_Rethrow, e você? 
    1. T-SQL é muito poderoso e eficiente dentro de seu objetivo primário, manipulação de conjunto de dados.
  4. Lixos T-SQL espalhados por aí. Essa é minha maior preocupação, se 70% dos desenvolvedores escrevem código C# ruim, 95% dos devs escrevem consultas T-SQL horríveis. E isso, desculpem o termo, FODE com o banco de dados e aplicação! Deixando de lado os problemas do EF V1 na geração de T-SQL, o EF 4.0 é bem mais eficiente e prefiro 1000 vezes a tradução do e-SQL do EF feito por uns indianos malucos (piadinha, ok?!) a deixar nossos desenvolvedores escreverem as consultas.
    1. Sim, talvez seja preconceito. Certa vez estava em uma apresentação sobre ORMs uma pessoa disse: “eu não quero pagar esse overhead de X milisegundos de tradução do EF porque todas as minhas consultas são bem escritas”. Bom, eu erro, e a menos que você seja um programador T-SQL fodístico-mega-flawless como esse camarada, um ORM pode ajudar.

 Agora a análise dos pontos que o Giggio citou: 
  1. Concordo em geral. Planos ficam guardados, tanto de consultas ad-hoc quanto de procedimentos e sim, existe statement level recompilation a partir do 2005. O SQL Server desde versões anteriores tenta fazer uma auto-parametrização das suas consultas ad-hoc, mas ele somente conseguirá fazer isso se a sua consulta possibilitar que um mesmo plano possa ser reutilizado de forma segura para TODOS os parâmetros possíveis. 
  2. Não é um mito, SP estimula a reusabilidade no contexto do banco de dados. O que eu concordo é que existem outras maneiras, possivelmente mais eficientes de atingir esse objetivo.
  3. Mesmo do item 2, não é um mito no contexto de banco de dados, mas posso aproveitar bem minha aplicação para isso.
  4. Discordo. No SQL Server existe uma coisa chamada ownership chaining, que permite criar um cenário onde um usuário não tenha acesso direto a tabela (nada de selects ou CUDs na tabela) e através de uma procedure com mesmo “dono” o usuário com permissão de EXEC na proc possa chegar a tabela. É uma característica que nos permite um bom controle.
    1. Não vou entrar em detalhes aqui das aplicações mal desenvolvidas. Quero me jogar da janela toda vez que alguém vai instalar um novo software e o camarada me pede: preciso de SA ou dbowner. Normalmente isso acontece porque a empresa que desenvolveu o software não utilizou o princípio de desenvolver com menor privilégio possível e não sabe (isso mesmo, é ignorância sobre seu próprio software) quais são as reais permissões necessárias. AAARRRGGGHHH.
  5. É uma verdade, só acho que esse é um argumento ridículo em termos de otimização. Se você está falando que seu ambiente está tão otimizado que você está considerando esse ganho ínfimo, por favor, me chame para conhecer seu ambiente que faço uma revisão grátis da saúde do SQL Server (agora, se eu encontrar um problema maior, cobro em dobro as horas, ok?). 
Sinceramente, eu acho que a discussão tem que sair um pouco do plano do mito (isso é bom para incentivar uma guerrilha :-)), mas sim no seguinte: eu posso fazer algumas dessas coisas de forma mais eficiente ou, tão-eficiente quanto, usando essa outra abordagem: ORM.

Alguns comentários sobre os comentários... 
  • ETL – Esqueçam ETL x ORM, não trabalham juntos. Reporting não é ETL. Realmente não entendi essa viagem sobre ETL. Seu ORM ou SP garantem que o dado JÁ está consistente com a regra de negócio no banco de dados, dados consistentes é isso que eu preciso, agora me deixem em paz que eu vou trabalhar diretamente com os dados, seja para uma carga de DW ou outra coisa, ponto final.
    • E DUVIDO que alguém vai ficar no SSIS customizando um script component como source só para reutilizar uma DLL com um EF lá dentro (exmeplo). Não me parece muito esperto não...
  • Gostei das respostas do Ronaldo Tolentino. A realidade hoje é outra e temos muito legado, então regras dentro de SPs permitem que diferentes sistemas estejam em acordo e eu possa mudar a regra em somente um lugar.
    • Sei que posso usar uma DLL, refazer a distribuição entre aplicações diferentes, carregá-la dinamicamente no AppDomain, MAS, caímos na questão da manutenção. Tenho expertise para tal? Como vou controlar as diferentes versões dos diferentes sistemas? A troca da DLL trará um efeito colateral?
    • Discordo do Giggio quando ele diz que existem maneiras mais simples, existem sim outras maneiras, mas a facilidade é um termo subjetivo de acordo com nossa experiência.
  • A frase “No banco de dados, a unica coisa que vc otimiza é as procedures, agora na aplicação vc tem inumeras arquiteturas q lhe possibilitam isso”.
    • Ah Danimar, Danimar, se fosse só isso eu estaria desempregado... 

Conclusão: DEPENDE!
É idiota, mas verdade. A diferença está quando você responde “depende” sabendo analisar criticamente seu cenário ou quando você responde depende e começa a falar bobagens. Vocês podem ficar brigando dias e dias, mas o que serve para um pode ser horrível para o outro.  
Não sou purista e hoje trabalho com uma abordagem híbrida. Utilize seu ORM para aquele “front-end básico de BD” (90% da aplicação que o desenvolvedor júnior consegue programar) e quando houver relatórios em tempo real ou operações complexas que sejam data intensive, coloque em SPs e apresente isso como uma função na entidade mais adequada. Sem dor. 
No mais gostei do post e das discussões, bom para aquecer um início de dia...
 
[]s
Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm
http://www.srnimbus.com.br/

terça-feira, 3 de agosto de 2010

Finalmente, SQL Server 2008 Internals em Brasília

Bom dia pessoal.
Depois de já ter ido para São Paulo e estar com boa demanda de outros estados, resolvi abrir uma turma para o curso SQL03 - SQL Server 2008 Internals em Brasília, pois a partir de outubro estarei impossibilitado de fazer viagens por alguns meses.

Estando em casa e sendo a primeira vez na minha cidade natal, resolvi fazer um desconto e um horário diferente, para tentar atender aqueles que não conseguem liberação total do trabalho.

/*
Mais informações:

* Alterei a data do treinamento para início de outubro.
* A princípio vou trabalhar com uma turma menor e realizar o treinamento dentro da Sr. Nimbus (708/708 norte), que conta com um espaço bem confortável.
* O treinamento está confirmado! Então garanta logo a sua vaga.

Caso a demanda cresça um pouco mais, ou abrirei uma segunda turma em outra data, ou podemos nos deslocar para outro centro de treinamento, também próximo do atual, para aqueles que estão vindo de fora de Brasília.
*/

O curso acontecerá entre 04/10 e 08/10/2010, entre 14:00h e 22:30h. Isso mesmo, será ministrado de tarde e de noite, então sendo somente uma semana posso atender aqueles que vem de fora e você só precisa conseguir uma liberação parcial do trabalho (apesar de eu recomendar dedicação exclusiva para o curso).

O curso é puxado, denso e detalhado, então vamos levantar a barra e separar os meninos dos homens (ou mulheres).

Os detalhes do curso, ementa, investimento, estão aqui.



[]s
Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm
www.srnimbus.com.br

quinta-feira, 22 de julho de 2010

Não sofra depois, aperte o cinto logo no começo...

Com o passar do tempo em que você fica dentro de um único cliente, e quanto mais entende do negócio, sua importância aumenta exponencialmente dentro do projeto ou na integração desses projetos.

Por um lado essa situação é super interessante, pois é criado um elo de confiança, um relacionamento duradouro entre as empresas e, muito provavelmente, você melhore a efetividade do seu trabalho e dos resultados. Por outro lado, existe uma boa chance de que você acabe se tornando um ponto focal para solução de problemas e novas demandas, que vai impactar diretamente o gerenciamento do seu trabalho, pois o número de interrupções cresce demais e aquele trabalho técnico isolado fim a fim (que tanto gostamos de fazer), acaba sendo atrapalhado.


Particularmente eu estou vivendo essa situação que é muito gratificante e, como sempre, aprendendo demais com novos trabalhos, então resolvi compartilhar um aprendizado com vocês...


Estou trabalhando com o CRM 4.0 da Microsoft e carregando uma série de dados (oriundos de fontes diversas) utilizando como canal de entrada os web services publicados pelo produto. Recentemente codifiquei tudo o que precisava para importar uma entidade específica e larguei a rotina em background no servidor, fazendo a importação dos dados para essa entidade, que terá alguns milhões de registros.


Em um primeiro momento estávamos com uma média de 36.000 registros por hora, o que me dizia que em dez horas eu teria importado aproximadamente 360.000 registros. Como a rotina de inserção faz algumas chamadas de validação, para evitar - por exemplo - registros com um campo específico duplicado, essas rotinas ficariam mais pesadas com o tempo (pois aumentaria o volume de registros para validação dentro do CRM) e imaginei que o processamento efetivamente iria demorar mais do que um número X de dias estimados.


Com as inserções em andamento, o que eu previa aconteceu. Com o passar do tempo o número de registros importados por hora caiu, fazendo que a importação passasse a demorar mais tempo. Eu, com outras demandas (sempre urgentes e para ontem - vide parênteses abaixo) pensei: larga esse trem lá em background enquanto eu toco as outras tarefas, está ficando cada vez mais lento, mas me parece ser o comportamento normal.


Abre parênteses: ODEIO quando as pessoas que insistem em falar "ISSO É PARA ONTEM". Por acaso você têm uma máquina do tempo? Vai usar o delorean e viajar para o passado e fazer a entrega? Então animal da cauda, que tal parar de irritar as pessoas falando isso e discutir estratégias e novos prazos para você conseguir o que quer? Se era para ontem, ou não foi comunicado, planejado ou alguém cometeu um erro de estimativa, mas não importa, entenda o que aconteceu e foque na solução. Fecha parênteses.


Nesse meio tempo a importação ficou cada vez mais lenta, chegando a ficar quase 10 vezes mais devagar que a velocidade original. Achei bem estranho os números que estava vendo, mas sem tempo de analisar se o comportamento (e bem ou mal estava rodando), dei prioridade para outras tarefas, até esse fim de semana, onde o processo insistiu em sofrer com exceções de timeout do CRM.


Sem ter para onde correr, fui em busca da raiz o problema: VS debugger, profiler, tudo rodando direitinho e sabe o que eu descobri? Um índice fundamental para minha pesquisa no SQL Server não estava criado na tabela que representa a entidade! Pelo padrão de implementação que estamos utilizando, este índice já deveria ter sido criado, mas na falta dele as consultas acabavam em um lindo table scan.


Criado o índice para suportar a consulta, voltamos à taxa de importação que eu via no início do processamento (quando um table scan era baratinho, baratinho). Tudo resolvido, voltei para as outras atividades com essa experiência me cutucando (doida para chegar ao blog).


O interessante é que durante o fim de semana mandei um e-mail para um dos envolvidos no projeto, falando sobre o problema de timeout, e ontem fomos conversar quando ele disse: "Uma das ações que vou fazer é aumentar o timeout.". Sabe qual foi minha resposta: "Não, eu quero que você coloque o valor mais baixo possível para o timeout!" (foi curioso a cara de espanto do meu amigo).


Entendeu o motivo da minha requisição? Espero que sim...



A lição


Trabalhe (principalmente desenvolva) sempre com os menores limites possíveis, isto é, seja o mais restritivo possível nos tempos de resposta.


Passamos uma semana com uma importação rendendo pouco, muito pouco, mas como a coisa estava funcionando e rodando em background, eu priorizei outras atividades. Quando a tarefa realmente parou, eu precisei de menos de uma hora para corrigir o problema e voltar com uma importação dez vezes mais rápida. Acabei adiando o fim da importação em alguns dias por não ter gastado uma hora (OK, não tinha como adivinhar que seria somente uma horinha) para ver se a demora da importação era natural.


No que toca o CRM, se o timeout desde o início estivesse sido colocado bem baixo, o problema teria acontecido antes e com certeza hoje eu já estaria com todos os milhões de registros importados. Se pensarmos no desenvolvimento de novas soluções, é mais fácil você trabalhar com massas de dados e distribuições de recursos menores no desenvolvimento do que na produção. Aí talvez passe por aquele conhecido problema: "nossa, no desenvolvimento essa tarefa é tão rápida".


Então se no desenvolvimento você restringir bastante os seus timeouts (dos testes de unidade, acesso a dados - SQLConnection e SQLCommands -, chamadas a web services, etc.) provavelmente irá encontrar e resolver mais cedo alguns problemas que te assombrariam em produção. A partir de agora vou apertar o cinto logo no começo!



Melhor dificultar a coisa na sua máquina e resolver mais problemas, do que assistir de camarote quando, em produção, estiverem milhares de usuários acompanhando sua aplicação falhar... Pense nisso!


[]s
Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm
http://www.srnimbus.com.br/