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