A nova propriedade “Actual Rows Read” nos planos de execução (que no SQL Server Management Studio é exibida como “Number of Rows Read”) foi uma adição bem-vinda aos sintonizadores de desempenho. É como ter uma nova superpotência, poder dizer o significado do Predicado de Busca versus o Predicado Residual dentro de um operador de Busca. Eu amo isso, porque pode ser realmente significativo para a consulta.
Vejamos duas consultas, que estou executando no AdventureWorks2012. Eles são muito simples – um lista pessoas chamadas John S e o outro lista pessoas chamadas J Smith. Como todas as boas agendas telefônicas, temos um índice em LastName, FirstName.
select FirstName, LastName from Person.Person where LastName like 'S%' and FirstName = 'John'; select FirstName, LastName from Person.Person where LastName = 'Smith' and FirstName like 'J%';
Caso você esteja curioso, eu recebo 2 linhas do primeiro e 14 linhas do segundo. Na verdade, não estou tão interessado nos resultados, estou interessado nos planos de execução.
Vamos ver o que está acontecendo. Abri uma cópia mais antiga do SQL Sentry Plan Explorer e abri meus planos lado a lado. Aliás – eu tinha executado as duas consultas juntas e ambos os planos estavam no mesmo arquivo .sqlplan. Mas eu poderia abrir o mesmo arquivo duas vezes no PE e colocá-los lado a lado alegremente em grupos de guias.
Excelente. Parecem iguais! Eu posso ver que o Seek à esquerda está produzindo duas linhas em vez de quatorze – obviamente esta é a melhor consulta.
Mas com uma janela maior, eu teria visto mais informações e foi uma sorte ter executado as duas consultas no mesmo lote.
Você pode ver que a segunda consulta, que produziu 14 linhas em vez de 2 linhas, foi estimada em mais de 80% do custo! Se eu executasse as consultas separadamente, cada uma estaria me mostrando 100%.
Agora vamos comparar com a versão mais recente do Plan Explorer.
A única coisa que salta para mim imediatamente é o aviso. Vejamos um pouco mais de perto.
O aviso diz “A operação causou E/S residual. O número real de linhas lidas foi 2.130, mas o número de linhas retornadas foi 2.” Com certeza, mais adiante vemos “Real Rows Read” dizendo 2.130 e Actual Rows em 2.
Uau! Para encontrar essas linhas, tivemos que procurar 2.130?
Veja, a maneira como a Busca é executada é começar pensando no Predicado da Busca. Esse é o que alavanca bem o índice e que realmente faz com que a operação seja uma Busca. Sem um Predicado de Busca, a operação se torna uma Varredura. Agora, se este predicado de busca for garantido para ser no máximo uma linha (como quando tem um operador de igualdade em um índice exclusivo), então temos uma busca Singleton. Caso contrário, temos uma varredura de intervalo, e esse intervalo pode ter um prefixo, um início e um fim (mas não necessariamente um início e um fim). Isso define as linhas na tabela que nos interessam para o Seek.
Mas “interessado em” não significa necessariamente “devolvido”, porque podemos ter mais trabalho a fazer. Esse trabalho é descrito no outro Predicado, que muitas vezes é conhecido como Predicado Residual.
Agora que o Predicado Residual pode realmente estar fazendo a maior parte do trabalho. Certamente está aqui - está filtrando as coisas de 2.130 linhas para apenas 2.
O Range Scan começa no índice em “John S”. Sabemos que se houver um “John S”, esta deve ser a primeira linha que pode satisfazer a coisa toda. “Ian S” não pode. Assim, podemos pesquisar no índice nesse ponto para iniciar nossa varredura de intervalo. Se olharmos para o XML do Plano, podemos ver isso explicitamente.
Observe que não temos um prefixo. Isso se aplica quando você tem uma igualdade na primeira coluna dentro do índice. Temos apenas StartRange e EndRange. O início do intervalo é "Maior que ou igual" (GE) ScanType, no valor "S, John" (as referências de coluna fora da tela são LastName, FirstName) e o final do intervalo é "Menor que" ( LT) o valor T. Quando a varredura atinge T, está feito. Nada mais a fazer. O Seek agora completou sua varredura de alcance. E neste caso, retorna 2.130 linhas!
Exceto que na verdade não retorna 2.130 linhas, apenas lê 2.130 linhas. Nomes como Barry Sai e Ken Sánchez são lidos, mas apenas os nomes que satisfazem a próxima verificação são retornados – o Predicado Residual que garante que o Nome seja John.
A entrada Real Rows Read nas propriedades do operador Index Seek nos mostra esse valor de 2.130. E embora seja visível em versões anteriores do Plan Explorer, não recebemos um aviso sobre isso. Isso é relativamente novo.
Nossa segunda consulta (procurando por J Smith) é muito melhor, e há uma razão pela qual foi estimado ser mais de 4 vezes mais barato.
Aqui sabemos exatamente o LastName (Smith), e o Range Scan está no FirstName (J%).
É aí que entra o prefixo.
Vemos que nosso Prefixo é um operador de Igualdade (=, ScanType=”EQ”), e que LastName deve ser Smith. Ainda nem consideramos o início ou o fim do intervalo, mas o prefixo nos informa que o intervalo está incluído na parte do índice em que LastName é Smith. Agora podemos encontrar as linhas>=J e
Ainda há um Predicado Residual aqui, mas isso é apenas para garantir que “LIKE J%” seja realmente testado. Embora nos pareça intuitivo que “LIKE J%” seja exatamente equivalente a “>=J e
Antes do Service Pack 3 do SQL Server 2012, não tínhamos essa propriedade e, para ter uma ideia da diferença entre as linhas reais lidas e as linhas reais, precisávamos usar o sinalizador de rastreamento 9130. Aqui estão esses dois planos com esse TF ligado:
Você pode ver que não há aviso desta vez, porque o operador Seek está retornando todas as 2130 linhas. Acho que se você estiver usando uma versão do SQL Server que dá suporte a esta leitura de linhas reais, você deve parar de usar o sinalizador de rastreamento 9130 em suas investigações e começar a examinar os avisos no Plan Explorer. Mas, acima de tudo, entenda como seus operadores fazem suas coisas, porque assim você poderá interpretar se está satisfeito com o plano ou se precisa agir.
Em outro post, mostrarei uma situação em que você pode preferir que as Linhas reais lidas sejam maiores que as Linhas reais.
@rob_farley