Suponha que haja uma tabela com índice clusterizado
create table [a_table] ([key] binary(900) unique clustered);
e alguns dados
insert into [a_table] ([key])
select top (1000000) row_number() over (order by @@spid)
from sys.all_columns a cross join sys.all_columns b;
Ao inspecionar as estatísticas de armazenamento desta tabela
select st.index_level, page_count = sum(st.page_count)
from sys.dm_db_index_physical_stats(
db_id(), object_id('a_table'), NULL, NULL, 'DETAILED') st
group by rollup (st.index_level)
order by grouping_id(st.index_level), st.index_level desc;
alguem pode ver
index_level page_count
----------- ----------
8 1
7 7
6 30
5 121
4 487
3 1952
2 7812
1 31249
0 125000
NULL 166659
essa tabela ocupa 166659 páginas no total.
No entanto, a varredura da tabela
set nocount on;
set statistics io, time on;
declare @cnt int;
select @cnt = count(1) from [a_table];
set statistics io, time off;
produz
Table 'a_table'. Scan count 5, logical reads 484367, ...
CPU time = 1757 ms, elapsed time = 460 ms.
número quase três vezes maior de leituras lógicas em relação ao espaço ocupado pela tabela. Quando examinei o plano de consulta, notei que o SqlServer usava a varredura de índice paralela. E é aqui que surge a primeira parte da questão.
Como a varredura de índice paralela é executada para fazer com que o SqlServer faça tantas leituras lógicas?
Especificando option (maxdop 1)
para suprimir o paralelismo
set nocount on;
set statistics io, time on;
declare @cnt2 int;
select @cnt2 = count(1) from [a_table] option (maxdop 1);
set statistics io, time off;
resultou em
Table 'a_table'. Scan count 1, logical reads 156257, ...
CPU time = 363 ms, elapsed time = 367 ms.
A comparação de estatísticas para varredura de índice paralela e não paralela neste caso leva à conclusão de que às vezes é melhor evitar a varredura de índice paralela. E é aqui que surge a segunda parte da questão.
Quando devo me preocupar com a varredura de índice paralelo? Quando deve ser evitado/suprimido? quais são as melhores práticas?
Os resultados acima são obtidos em
Microsoft SQL Server 2014 (SP2) (KB3171021) - 12.0.5000.0 (X64)
Se você adicionar um
TABLOCK
ouREADUNCOMMITTED
dica à tabela, obterá uma varredura ordenada por alocação , que relatará o mesmo número de leituras lógicas que o número de páginas na tabela.Com uma varredura ordenada por alocação, o SQL Server usa estruturas de alocação para direcionar a distribuição de páginas entre threads. Os acessos à página IAM não são contados em
STATISTICS IO
.Para uma varredura não ordenada por alocação, o SQL Server divide a varredura em subintervalos usando chaves de índice. Cada operação interna para localizar e distribuir uma nova página ou intervalo de páginas (com base em um intervalo de chaves) para um thread de trabalho requer o acesso aos níveis superiores da árvore b. Esses acessos são contados por
STATISTICS IO
, assim como os acessos de nível superior feitos por threads de trabalho ao localizar o ponto inicial de seu intervalo atual. Todas essas leituras extras de nível superior são responsáveis pela diferença que você vê.Muito peso é dado às estatísticas de I/O na minha opinião. Ajuste suas consultas de acordo com o que é importante para você e sua carga de trabalho. Isso geralmente é tempo decorrido, mas a utilização de recursos também pode ser um fator. Nenhuma métrica é 'melhor' - você deve ter uma visão equilibrada.