Estratgias de
indexao
Janeiro de 2013, Pedro Oliveira
Introduo
Este documento descreve a utilizao dos objetos de gesto dinmicos relacionados com indexao disponibilizados pelo
SQL Server para definir uma estratgia de indexao correta.
Keywords
SQL Server, Index, Tuning, DMV, DMO
Pg. 1
Estratgias de indexao
Para os ndices que esto a ser usados, qual o padro de utilizao/acesso? (sys.dm_db_index_operational_stats)
A mesma coluna que est em 3 ou mais ndices compostos tambm est indexada individualmente
Em resumo, uma grande confuso, mas no se pode simplesmente eliminar ndices que o nosso instinto diz que no
so usados. A partir do SQL Server 2005, atravs dos DMOs de indexao, existe uma viso adequada sobre os ndices que
so usados e aqueles que o optimizador ignora.
No entanto, antes de comear a explicar as scripts que podemos usar para obter esta informao, convm dizer que seguir
cegamente os conselhos dos DMOs tambm no o caminho correto. Como foi referido anteriormente, definir uma
estratgia eficiente de indexao uma arte delicada e que requer conhecimento da base de dados, de como os dados
esto distribudos pelas tabelas e como so obtidos pelas pesquisas. No faz parte do mbito deste artigo fornecer um
tutorial completo sobre como determinar um conjunto eficaz de ndices, mas sim obter informao sobre o que est bem
e o que poder estar mal na base de dados.
Pg. 2
Estratgias de indexao
ndices abrangentes
Um ndice abrangente ou covering index contm todas as colunas e dados necessrios para a pesquisa. Isto significa
que as colunas usadas em join ou nas condies das pesquisas so includas no ndice, juntamente com as colunas que
so simplesmente selecionadas. As ltimas devem ser includas como colunas de INCLUDE em vez de fazerem parte
efetiva do ndice. Se um ndice abrange uma pesquisa, significa que o optimizador pode obter todos os dados usando
unicamente o ndice, sem necessidade de pesquisar a tabela toda (table scan) ou fazer key lookup para obter dados
do ndice clustered que no esto no ndice abrangente. Isto significa se sero feitas poucas leituras e normalmente
o mtodo mais rpido e eficiente para obter os dados. Est realado normalmente pois mesmo que o ndice exista
no significa que o optimizador o vai escolher para obter os dados.
Alta seletividade
Se escolhemos uma coluna com baixa seletividade para o ndice (pouca diversidade de valores que implica que um
valor devolve muitas linhas), ento o optimizador poder decidir fazer uma pesquisa tabela toda (table scan) para
obter os dados. Pesquisar uma tabela mau, mas isto porque normalmente significa ler muitas linhas de dados, mas
em tabelas pequenas por vezes pesquisar nas linhas todos poder ser mais rpido que pesquisar num ndice.
A primeira coluna de cada ndice dever ter alta seletividade. No entanto isto no significa que cada ndice deva
comear pela(s) coluna(s) da chave primria, dever ser uma coluna que seja pesquisada.
Pg. 3
Estratgias de indexao
vezes, tambm pesquisa juntamente com o Nome, ento um ndice sobre (Apelido, Nome) ir satisfazes ambas as
pesquisas.
object_id identificador da tabela ou vista a que pertence o ndice, nico por base de dados
index_id identificador do ndice, nico ao nvel do objeto (tabela ou vista); um index_id de 0 significa heap (sem
ndice clustered na tabela); um index_id de 1 est sempre associado ao ndice clustered; index_id superiores a 1
esto sempre associados a ndices no clustered
user_seeks o nmero de vezes que o ndice foi usado numa operao de seek, para encontrar uma linha
especfica
user_scans o nmero de vezes que o ndice foi usado num scan de pginas para obter dados (percorrer os
dados todos)
user_lookups unicamente para ndices clustered, o nmero de vezes que um ndice foi usado como bookmark
lookup para obter a linha de dados, isto porque o ndice no clustered no tinha os dados necessrios e precisou
de os obter do ndice clustered usando o apontador
user_updates o nmero de vezes que um ndice foi modificado devido a alteraes de dados na tabela
Para cada coluna de ao do utilizador (user_*) h tambm uma coluna correspondente ltima ao (last_user_*) que tem
a data e hora da execuo da ltima ao. Tambm h colunas relativas utilizao das aes por parte do sistema
(system_* e last_system_*).
A informao nesta DMV cumulativa, e limpa quando o servidor reiniciado ou o ndice eliminado e recriado. As
estatsticas mantm-se quando o ndice reorganizado ou recriado, e mesmo quando desativado e recriado. Consultas a
esta DMV devolvem todos os ndices (incluindo heaps e ndices clustered) que tenham sido lidos ou escritos pelo menos
uma vez. Se um ndice existe mas nunca foi usado desde a sua criao, ou desde que as estatsticas foram atualizadas, a
sua informao no aparece nesta DMV. uma DMV que devolve dados de toda a instncia SQL, pelo que, para uma
utilizao especfica, se dever restringir a uma base de dados, usado a coluna database_id.
Pg. 4
Estratgias de indexao
A seguinte consulta disponibiliza uma listagem dos ndices das bases de dados que foram usados pelo menos uma vez
numa pesquisa, ordenado pelos ndices com mais scans. Um nmero elevado de scans pode indicar a necessidade de
atualizar as estatsticas para uma determinada tabela ou ndice. No entanto, igualmente, um nmero elevado de scans
pode ocorrer se o optimizador decidir que a tabela suficientemente pequena de tal forma que mais rpido um scan
que um seek. Logo, o resultado desta consulta no dever ser analisado isoladamente, mas juntamente com a informao
referente seletividade e tamanho do ndice (que podem ser obtidos atravs da sys.dm_db_index_physical_stats).
SELECT OBJECT_NAME(ddius.[object_id], ddius.database_id) AS [object_name] ,
ddius.index_id ,
ddius.user_seeks ,
ddius.user_scans ,
ddius.user_lookups ,
ddius.user_seeks + ddius.user_scans + ddius.user_lookups AS user_reads ,
ddius.user_updates AS user_writes ,
ddius.last_user_scan ,
ddius.last_user_update
FROM sys.dm_db_index_usage_stats ddius
WHERE ddius.database_id > 4 -- retirar as tabelas do sistema
AND OBJECTPROPERTY(ddius.OBJECT_ID, 'IsUserTable') = 1 -- obter informao s de tabelas do utilizador
AND ddius.index_id > 0 -- eliminar heaps
ORDER BY ddius.user_scans DESC
De notar que nesta consulta e nas restantes que sero apresentadas, ser usada a seguinte formula para calcular o total de
vezes que um ndice foi usado pelo optimizador para responder a uma pesquisa:
[user_seeks] + [user_scans] + [user_lookups] = [user reads]
A coluna user_updates por si s devolve o nmero de vezes que o ndice foi atualizado como resultado da modificao de
dados (escritas). De uma perspetiva de performance tuning, o valor desta DMV inestimvel pois mostra exatamente a
utilizao dos ndices e, uma coisa que as verses mais antigas do SQL Server no faziam, quais os ndices que no esto a
ser usados ou esto a ser usados somente em atualizaes. Um clculo similar pode ser usado para anlise de leituras e
escritas por parte do sistema, mas vamos ignorar a atividade do sistema pois praticamente insignificante comparada
com a atividade do utilizador.
Nas seces seguintes iro ser apresentadas scripts para:
Estes ndices so candidatos para remoo, depois de uma anlise mais profunda. Nunca se deve remover ndices
cegamente, e deve-se ter a certeza que o ndice no realmente usado (h ndices que podem ser usados poucas vezes,
sendo no entanto crticos para anlises mensais, semestrais, anuais, ) antes de decidir remov-lo.
Pg. 5
Estratgias de indexao
Se o SQL Server estiver a executar h tempo suficiente para ter uma completa e representativa carga, h uma boa
hiptese que esses ndices (e at tabelas) estejam mortas, o que significa que no so usadas pela base de dados e
podem ser potencialmente removidas.
Pg. 6
Estratgias de indexao
Para gerar o comando de remoo dos ndices, basta incluir o seguinte texto no final do SELECT:
'DROP INDEX [' + i.[name] + '] ON [' + su.[name] + '].[' + o.[name]
+ '] WITH ( ONLINE = OFF )' AS [drop_command]
Depois de verificar a necessidade de remover um ndice na base de dados, basta copiar o comando DROP INDEX do
resultado da consulta para uma janela nova e executar. Como sempre, indicado testar na base de dados de
desenvolvimento antes de executar em produo. Alm disso, recomendado fazer uma cpia de segurana da base
de dados antes de executar os comandos.
Tal como referido anteriormente, este artigo no para incentivar a remover grande nmero de ndices sem uma
anlise adequada. Para comear, aconselhvel verificar quando as estatsticas de utilizao foram criadas (h quanto
tempo o servio de SQL Server est a executar):
SELECT DATEDIFF(DAY, sd.crdate, GETDATE()) AS days_history
FROM sys.sysdatabases sd
WHERE sd.[name] = 'tempdb' ;
Alm disso, um ndice pode no ter sido usado recentemente simplesmente porque a sua funcionalidade de
natureza cclica (executa s ms a ms para processamentos especficos, por exemplo), ou simplesmente porque um
ndice recente. Mais uma vez, importante no remover ndices sem fazer os testes adequados num ambiente no de
produo.
Pg. 7
Estratgias de indexao
necessrio garantir que a instncia SQL Server est a correr h tempo suficiente para ter a carga/utilizao tpica nas
estatsticas. Mais uma vez no convm esquecer os ndices de natureza cclica que podero no aparecer na utilizao
do dia-a-dia. Embora esses ndices sejam pouco usados, podem ser, e normalmente so, crticos.
page_latch_wait_count, page_latch_wait_in_ms nmero de vezes e tempo de espera por uma pgina fsica at
que a conteno foi removida (latch)
Esta DMF tem mais colunas interessantes como por exemplo as lob_* que do valores relativos a objetos LOB.
Pg. 8
Estratgias de indexao
Depois de rever os dados devolvidos neste exemplo, bastante visvel que alguns ndices esto a ser bombardeados
por inseres enquanto o utilizador no tem qualquer benefcio relativamente a leituras. H que fazer algo sobre este
tipo de dados.
Pg. 9
Estratgias de indexao
Bloqueios de conteno ocorrem quando o motor l uma pgina fsica. Aps fazer isso, pede um bloqueio, pesquisa a
pgina, l a linha e depois liberta o bloqueio quando a pgina requisitada por outro processo. Este mtodo
chamado bloqueio preguioso (lazy latching).
Apesar do bloqueio de conteno ser um processo benigno, interessante ter a informao que esta consulta
devolve. Permite-nos identificar quais dos nossos ndices esto a encontrar um nmero significativo de esperas
quando tentam pedir um bloqueio, devido a j ter sido pedido por outro processo. Bloqueios de I/O acontecem em
operaes de transferncia entre disco/memria e elevadas contagens de bloqueios de I/O pode ser o reflexo de
problemas a nvel do sistema de discos, especialmente quando os tempos de espera so superiores a 15ms.
Pg. 10
Estratgias de indexao
De notar a juno com a sys.dm_db_missing_index_details para identificar se h alguma sugesto de ndice que possa
resolver o bloqueio. Antes de implementar qualquer novo ndice deve ser bem testado.
Pg. 11
Estratgias de indexao
ndices em falta
Quanto o optimizador de pesquisas gera um plano de execuo, determina qual o caminho de acesso a dados timo que
ir satisfazer os critrios de pesquisa, e ento verifica se h algum ndice existente que tenha esse caminho (ou parecido).
Se o ndice ideal no existe, o optimizador escolhe o melhor disponvel, ou simplesmente faz um varrimento tabela (table
scan), mas guarda o detalhe do ndice que falta. Esta informao disponibilizada atravs de 4 DMOs
sys.dm_db_missing_index_*:
sys.dm_db_missing_index_columns a DMF que recebe um parmetro, index_handle, e devolve uma tabela com
informao sobre as colunas que deveriam constituir o ndice
A primeira coisa que salta vista o facto de no existir a coluna index_id em qualquer das DMOs. Isto deve-se aos
resultados serem recomendaes para ndices que ainda no foram criados.
Os dados destes DMOs so reiniciados quando o servidor reiniciado. Por isso muito importante preservar os dados
acumulados, guardar em tabelas fsicas para anlise prpria, para garantir a sua existncia constante. preciso garantir
que quando estes dados so usados a sua informao representativa de uma carga completa.
Alm disso, os dados tambm so volteis e baseados nas pesquisas atuais. Se for implementado um ndice numa tabela
ou vista, os dados nos DMOs para esse objeto podero j no ser vlidos, inclusive podero j ter sido eliminados.
Pg. 12
identifica
os
ndices
em
falta,
devolve
as
colunas
Estratgias de indexao
index_group_handle o identificador do grupo de ndices que usado para relacionar a linha com a DMV
sys.dm_db_missing_index_group_stats
unique_compiles nmero de planos que foram compilados e poderiam ter usado o ndice
user_seeks nmero de operaes seek que poderiam ter usado o ndice
user_scans nmero de operaes scan que poderiam ter usado o ndice
last_user_seek ltima operao de seek que poderia ter usado o ndice
last_user_scan ltima operao de scan que poderia ter usado o ndice
avg_total_user_cost mdia do custo que as pesquisas poupariam caso o ndice existisse
avg_user_impact percentagem estimada de melhoria do custo nas pesquisas caso o ndice seja criado
Para cada uma das colunas de utilizador existe tambm a equivalente para o sistema, que regista quando o ndice seria
usado para operaes do sistema.
As colunas last_user_* so importantes pois ajudam a determinar se realmente os ndices sugeridos so necessrios. Se
no forem o suficientemente recentes ento provvel que a pesquisa fosse ad hoc ou no faa parte de uma carga
normal, e assim sendo o beneficio de criar o ndice pouco, ou nenhum.
Pg. 13
Estratgias de indexao
A seguinte consulta usa essa frmula para identificar ndices potencialmente uteis. Os dados apresentados, tal como j
foi referido anteriormente, so baseados na informao registada desde o ltimo restart ao servio do SQL Server.
SELECT user_seeks * avg_total_user_cost * ( avg_user_impact * 0.01 ) AS [index_advantage] ,
dbmigs.last_user_seek ,
dbmid.[statement] AS [Database.Schema.Table] ,
dbmid.equality_columns ,
dbmid.inequality_columns ,
dbmid.included_columns ,
dbmigs.unique_compiles ,
dbmigs.user_seeks ,
dbmigs.avg_total_user_cost ,
dbmigs.avg_user_impact
FROM sys.dm_db_missing_index_group_stats AS dbmigs WITH ( NOLOCK )
INNER JOIN sys.dm_db_missing_index_groups AS dbmig WITH ( NOLOCK )
ON dbmigs.group_handle = dbmig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS dbmid WITH ( NOLOCK )
ON dbmig.index_handle = dbmid.index_handle
WHERE dbmid.[database_id] = DB_ID()
ORDER BY index_advantage DESC ;
Pg. 14
Estratgias de indexao
Em qualquer caso, deve-se sempre analisar bem o impacto de criar e remover ndices num ambiente de testes.
Resumo
Discutimos os DMOs que ajudam a definir uma estratgia de indexao eficiente no SQL Server. Este uma das melhores
maneiras de garantir que as mais significantes e frequentes pesquisas de carga consigam ler os dados duma forma lgica
e ordenada por forma a evitar I/O desnecessrio. Para ter a melhor performance no SQL Server muito importante
conseguir encontrar o equilbrio correto entre demasiados e poucos ndices.
Viu-se como:
usar a DMV sys.dm_db_index_usage_stats DMV para descobrir ndices que existem mas nunca foram usados,
ou o custo de manuteno alto e o ndice raramente usado, o que no trs qualquer vantagem no
desempenho
usar a DMF sys.dm_db_index_operational_stats para obter estatsticas de utilizao de ndices para analisar
potenciais bloqueios ou conteno latch, ou excesso de I/O, tudo que pode causar os utilizadores esperarem
um tempo significativo para obter os dados
usar os DMOs sys.dm_db_missing_group para identificar ndices que o optimizador gostaria que estivessem
disponveis para obter um acesso timo aos dados de uma determinada pesquisa
Ao longo deste artigo salientou-se a necessidade de julgar com cuidado, aplicar o conhecimento da base de dados e da
sua informao e da carga normal, antes de criar ou remover qualquer ndice sugerido pelas consultas aos DMOs. Talvez a
mais importante seja garantir que o SQL Server est a executar h tempo suficiente para ter uma carga completa,
suficientemente representativa, para que as estatsticas e sugestes dos DMOs sejam suficientemente fidedignas.
Pg. 15