Apaixonado por tecnologia. Trabalho com tecnologia desde 2003.

Saiba mais sobre minha vida profissional aqui .

Fale comigo.
Siga-me no Twitter
Ultimos comentários
Calendário de Posts
<<  novembro 2017  >>
stqqssd
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

Siga o @DicaDoNerd no Twitter e acompanhe as novidades


Acredito que não usamos nem 30% do que o Banco de Dados SQL Server oferece. Há alguns dias, conversava com alguns amigos e lembrei de uma dica que a ordem de criação dos índices e consultas faz diferença, então, resolvi testar.

Tive uma surpresa, realmente faz diferença, algo que nem sempre nos atentamos, seguem as evidências dos testes.

Tamanho da tabela

clip_image002

Criação do índice (Notem que inclusive há botões, mover para cima e mover para baixo) .

clip_image002[8]

 

clip_image002[10]

clip_image002[12]

Resultado usando o índice corretamente. (CPF, Nome) Resultado usando o índice com a ordem invertida

O que achei mais engraçado é que o execution Plain apontou 50% para cada execução mostrando que o indice CPF_Nome foi acionado, mas, fez diferença no resultado (Select acima). Eu executei muitas vezes, pois, o Sql Server sempre mantém informações das consultas realizadas…

image

Não contente, fiz uma aplicação para comparar a execução e….. surpresa!!! Mesmo com o ExecutionPlain dizer que o custo era igual para ambos, a execução tem o tempo diferente.

image

Se for um cara chato como eu, vai logo pensar… Bom, depende de como foram feitos os testes… então vaí aí como foi realizado o teste.

class Program
{
static void Main(string[] args)
{

string strCnn = System.Configuration.ConfigurationSettings.AppSettings["cnn"];
Stopwatch timer = new Stopwatch();

for (int i = 0; i < 10; i++)
{
timer.Start();

using (SqlConnection cnn = new SqlConnection(strCnn))
{
using (SqlCommand cmd = new SqlCommand("Select * from Clientes where cpf like '%317395%' and nome like '%Minato%'", cnn))
{
cnn.Open();
var reader = cmd.ExecuteReader(CommandBehavior.SingleResult | CommandBehavior.CloseConnection);
}

}
timer.Stop();
Console.WriteLine("Utilizando Corretamente o Indice (CPf, Nome) " + timer.Elapsed);
timer.Reset();


timer.Start();

using (SqlConnection cnn = new SqlConnection(strCnn))
{
using (SqlCommand cmd = new SqlCommand("Select * from Clientes where nome like '%Minato%' and cpf like '%317395%'", cnn))
{
cnn.Open();
var reader = cmd.ExecuteReader(CommandBehavior.SingleResult | CommandBehavior.CloseConnection);
}

}
timer.Stop();
Console.WriteLine("Utiliando com a ordem invertida (Nome, CPF) " + timer.Elapsed);
timer.Reset();

}

Console.ReadKey();


}
}

 

Então fica a dica. ;-), vamos usar os indices na sequencia correta.

Sigam @DicaDoNerd.

 

Uma das dúvidas que sempre tive é como uma aplicação de grande porte baseada no LinqToSql se comportaria. Hoje trabalho em um segmento (bolsa de valores) onde performance é extremamente necessário. Sabemos que tabelas com grande concorrência sem a utilização do NOLOCK pode apresentar grandes problemas, este problema ainda não sei como resolver, mas uma coisa que consegui é estudar algumas soluções que podem melhorar a performance no LinqToSql.

A primeira cois foi criar uma tabela com tamanho razoável (Estou utilizando uma tabela de uma aplicação real) para tentar espelhar uma realidade (com tipos Nullables, campos preenchidos, não preenchidos e etc). A tabela contém mais de um milhão de registros (1.015.232) com 16 campos.

Select com a quantidade de registros (1.015.232) mais de um milhão registros
Tipo de dados e campos da tabela
Entidade criada a partir do LinqToSql (dbml)

É importante entender quando podemos desativar as propriedades ObjectTrackingEnabled e DeferredLoadingEnabled.

ObjectTrackingEnabled = Essa propriedade só deve ser desativada quando for uma operação de SELECT, ou seja, qualquer outra ação que manipule informações no banco de dados (INSERT, UPDATE ou DELETE), deve ter a propriedade habilitada, caso contrário, você irá se deparar com a seguinte exception.

Object tracking is not enabled for the current data context instance.

Exception disparada pela falta do ObjectTracking

Quando existe um relacionamento no banco de dados e você cria o ORM com o LinqToSql são criadas relações entre as tableas e você pode ter acesso a tabela relacionadas, por exemplo, se na tabela cliente houvesse uma tabela relacionada com tipo de cliente, porderíamos ter acesso ao tipo desta forma

ClienteEntidade.TipoClienteEntidade.TipoCliente

Ocorre que pode se tornar um problema quando há uma grande quantidade de registros, por uma boa prática devemos desabilitar este objeto e habilitarmos apenas quando necessário. Uma alternativa para carregar apenas a tabela necessária (quando há mais de duas tabelas relacionadas) é utilizar LoadWith .

Para desabilitar o mapeamento de entidades relacionadas é desabilitar a propriedade DeferredLoadingEnabled.

Agora vamos ver na prática se isso funciona mesmo. Vamos aos testes.

Como sou desconfiado de resultados baseado em uma unica execução, eu sempre acho que fica algo em cache, que fica algo na memória, enfim, fiz algumas repetições para garantir.

class Program
{
static void Main(string[] args)
{
Stopwatch timer = new Stopwatch();

int qtde = 0, qtdadeTracking = 0;
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Iteração: " + i);
timer.Start();
using (dbDadosDataContext db = new dbDadosDataContext())
{

qtde = db.ClientesEntidades.ToList().Count;
}
timer.Stop();
Console.WriteLine("Tempo com as propriedades habilitadas {0} qtade {1:N0}", timer.Elapsed, qtde);
timer.Reset();
timer.Start();
using (dbDadosDataContext db = new dbDadosDataContext())
{

db.DeferredLoadingEnabled = false; // Desabilitando as tabelas relacionadas.
db.ObjectTrackingEnabled = false; // Desabilito o tracking
qtdadeTracking = db.ClientesEntidades.ToList().Count;
}
timer.Stop();
Console.WriteLine("Tempo com as propriedades desabilitadas {0} - qtdade {1:N0}", timer.Elapsed, qtdadeTracking);
}
Console.ReadKey();

}
}

 

Resultado do teste: É possível diminuir pela metade o tempo de execução.

Espero que tenham gostado.

 
teste