Você está na página 1de 15

Estratgias de indexao

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

O contedo deste documento confidencial

Estratgias de indexao

Afinar a estratgia de indexao com as DMVs


Os ndices tm de ser eficientes. mau ter poucos ou demasiados. Os que existem devem garantir a leitura rpida dos
dados com o mnimo de I/O. Alm de um bom conhecimento de como a base de dados funciona, ajuda conhecer os
objetos de gesto dinmicos que existem para ajudar na estratgia de indexao.
Definir uma estratgia de indexao eficaz a nica maneira de garantir que as pesquisas mais significativas e frequentes
so capazes de ler apenas os dados necessrios, e de uma forma lgica e ordenada, devolvendo assim os dados rpida e
eficientemente, com um mnimo de I/O. No entanto, encontrar o equilbrio correto entre demasiados e poucos ndices, e
ter o conjunto correto de ndices, uma arte delicada. Requer conhecimento do desenho da base de dados, como os
dados esto distribudos entre as tabelas e os padres de pesquisa mais frequentes.
por isto que, do conjunto de objetos de gesto dinmicos (DMOs), os relacionados com indexao so provavelmente os
mais usados. Eles ajudam a responder s seguintes questes:

Existem ndices que j no so usados ou nunca foram usados? (sys.dm_db_index_usage_stats)

Para os ndices que esto a ser usados, qual o padro de utilizao/acesso? (sys.dm_db_index_operational_stats)

Quais so os ndices que esto em falta? (sys.dm_db_missing_index_details, sys.dm_db_missing_index_group_stats)

Melhores prticas das estratgias de indexao


Um cdigo SQL bem escrito l o mnimo possvel de dados das tabelas, devolve somente os dados necessrios para
satisfazer o pedido e usa lgica eficiente para manipular os dados por forma a sejam devolvidos na forma correta. No
entanto, independentemente de quo inteligentemente o SQL escrito, ele vai ler mais dados que os necessrios e ter
uma fraca performance caso no existam os ndices corretos. Mesmo que a pesquisa s devolva 20 linhas, com 3 colunas,
numa tabela com 100.000 linhas, o motor do SQL poder ter que ler as 100.000 linhas, caso no exista um ndice correto.
Os ndices podem aumentar a eficincia e performance de uma pesquisa, mas um ndice errado pode ser to mau quanto
um ndice correto bom. Se um ndice no usado, ele provoca sobrecarga em operaes de escrita: o motor do SQL tem
que escrever no ndice a cada insero, atualizao ou remoo de registos na tabela ou vista. Esta atividade gera
fragmentao que necessria corrigir atravs da reorganizao ou reconstruo dos ndices, mas estas operaes
consomem recursos importantes. Por isso muito importante saber o limite dos ndices em excesso e em falta.
Infelizmente encontramos situaes onde no existe indexao ou ento completamente catica:

Cada coluna est indexada individualmente

A mesma coluna que est em 3 ou mais ndices compostos tambm est indexada individualmente

No existe chave primria, mas quase todas as colunas tm ndices no clustered

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

O contedo deste documento confidencial

Estratgias de indexao

ndice clustered e chave primria


Basicamente, todas as tabelas no SQL Server devem ter um ndice clustered para permitir uma pesquisa eficiente dos
dados. Pode-se criar um clustered numa chave natural que reflete a forma como os dados sero pesquisados (Cdigo,
Nome, ) ou pode ser sobre uma coluna estreita, sempre incremental numrica (para minimizar a fragmentao) e
usar ndices no clustered para pesquisar os dados eficientemente. A maior parte dos conselhos apontam para a
ltima abordagem mas, independentemente disso, a maioria das tabelas devem ter um ndice clustered e uma chave
primria (que poder ser ou no o ndice clustered).

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.

Nem muitos nem poucos


Para abranger as pesquisas no significa que se devam criar ndices para todas elas. Se uma tabela tem muitas linhas e
est sujeita a alteraes frequentes, a presena de muitos ndices vai degradar a performance dessas alteraes, pois o
SQL Server tem que gerir os dados na tabela e nos ndices.
Devem-se examinar as caractersticas das pesquisas para uma determinada tabela, pois vai afetar o nmero de ndices
que queremos sobre ela. Se uma tabela bastante esttica podemos ter mais ndices, mas em tabelas sujeitas a
alteraes frequentes, queremos o mnimo de ndices possveis.

ndices estreitos (dentro do bom senso)


O caminho para abranger as pesquisas tambm no significa que se devam criar ndices largos, ndices com 16
colunas ou mais, na tentativa de abranger tudo de uma s vez. Se as colunas do ndice so grandes, cabem poucas
linhas numa pgina de dados, o ndice ocupar muito espao e a sua pesquisa ser ineficiente. Pesquisar ndices
estreitos mais rpido.
Mais uma vez, no entanto, requer um equilbrio. Ter um nmero elevado de ndices de uma nica coluna tambm
uma m ideia. O objetivo ter ndices o mais estreito possveis sendo usados no mximo de pesquisas possveis. Por
exemplo, se uma pesquisa feita sobre Apelido, um ndice sobre essa coluna boa ideia. Mas se o utilizador, por

Pg. 3

O contedo deste documento confidencial

Estratgias de indexao

vezes, tambm pesquisa juntamente com o Nome, ento um ndice sobre (Apelido, Nome) ir satisfazes ambas as
pesquisas.

Utilizar as DMOs de indexao


Entre outras coisas, o optimizador de consultas do SQL Server armazena meta-dados referentes ao uso de ndices
existentes. Quando o servio do SQL Server reinicia, essa informao perdida, por isso recomendado guardar a
informao numa tabela persistente para manter um conjunto de dados ricos para performance tuning. As consultas
apresentadas aqui e que implicam tomar decises para longo prazo, podem ser baseadas em dados que podero ter
somente dias ou horas; nunca se devem tomar decises numa base de informao to fraca. Convm ter informao
suficientemente antiga para tomar as decises certas.

As vistas do catlogo do sistema (DMVs)


Ocasionalmente vamos precisar de detalhes dos ndices e dos objetos em anlise, como o nome do ndice ou o tipo de
dados duma coluna do ndice, que no esto disponveis nas DMOs. Nessas ocasies vamos precisar de obter a
informao das vistas do catlogo do sistema. Por exemplo, podemos usar a sys.indexes para devolver dados do ndice
e configurao do mesmo, ou a sys.columns e sys.index_columns para obter informao detalhada sobre uma coluna
do ndice.

Analisar a utilizao de ndices (index_usage_stats)


Nesta seco vamos usar a DMV sys.dm_db_index_usage_stats, juntamente com outras vistas do catlogo do sistema, para
obter estatsticas sobre a utilizao dos ndices. Esta DMV disponibiliza, em particular, as seguintes colunas:

database_id identificador da base de dados

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

O contedo deste documento confidencial

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:

Encontrar ndices no sistema que nunca foram lidos nem escritos


Encontrar ndice que nunca foram lidos mas so mantidos (atualizados quando h modificao de dados)
Obter informao sobre a relao leituras/escritas, analisando aqueles em que a manuteno superior
utilidade em melhorar a performance da pesquisa

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

O contedo deste documento confidencial

Estratgias de indexao

Identificar ndices que nunca foram usados


A seguinte consulta usa a sys.indexes e sys.objects para obter tabelas e ndices na base de dados que no so
apresentados na sys.dm_db_index_usage_stats. Isto significa que esses ndices no tiveram escritas nem leituras desde
que o servio do SQL Server arrancou.
-- Listar ndices no usados
SELECT OBJECT_NAME(i.[object_id]) AS [Table Name] ,
i.name
FROM sys.indexes AS i
INNER JOIN sys.objects AS o ON i.[object_id] = o.[object_id]
WHERE i.index_id NOT IN ( SELECT ddius.index_id
FROM sys.dm_db_index_usage_stats AS ddius
WHERE ddius.[object_id] = i.[object_id] AND i.index_id = ddius.index_id AND database_id = DB_ID() )
AND o.[type] = 'U'
ORDER BY OBJECT_NAME(i.[object_id]) ASC ;

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.

Identificar ndices que so mantidos mas no usados


A seguinte consulta mostra ndices clustered e no clustered que esto a consumir recursos, relativamente a escritas e
manuteno, mas nunca so usados pelo optimizador para responder a pesquisas, pelo que nunca foram lidos. Usa a
tabela sys.indexes para obter o nome do ndice e a sys.partions para obter o total de linhas que o ndice tem.
SELECT '[' + DB_NAME() + '].[' + su.[name] + '].[' + o.[name] + ']' AS [statement] ,
i.[name] AS [index_name] ,
ddius.[user_seeks] + ddius.[user_scans] + ddius.[user_lookups] AS [user_reads] ,
ddius.[user_updates] AS [user_writes] ,
SUM(SP.rows) AS [total_rows]
FROM sys.dm_db_index_usage_stats ddius
INNER JOIN sys.indexes i ON ddius.[object_id] = i.[object_id] AND i.[index_id] = ddius.[index_id]
INNER JOIN sys.partitions SP ON ddius.[object_id] = SP.[object_id] AND SP.[index_id] = ddius.[index_id]
INNER JOIN sys.objects o ON ddius.[object_id] = o.[object_id]
INNER JOIN sys.sysusers su ON o.[schema_id] = su.[UID]
WHERE ddius.[database_id] = DB_ID() -- base de dados atual
AND OBJECTPROPERTY(ddius.[object_id], 'IsUserTable') = 1
AND ddius.[index_id] > 0
GROUP BY su.[name] ,
o.[name] ,
i.[name] ,
ddius.[user_seeks] + ddius.[user_scans] + ddius.[user_lookups] ,
ddius.[user_updates]
HAVING ddius.[user_seeks] + ddius.[user_scans] + ddius.[user_lookups] = 0
ORDER BY ddius.[user_updates] DESC ,
su.[name] ,
o.[name] ,
i.[name ]

Pg. 6

O contedo deste documento confidencial

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.

Identificar ndices ineficientes


A prxima consulta usa a DMV sys.dm_db_index_usage_stats e somente obtm ndices no clustered. Ajuda a decidir se
o custo de manter um ndice em particular superior ao benefcio que este d base de dados.
-- ndices potencialmente ineficientes (no clustered em que escritas > leituras)
SELECT OBJECT_NAME(ddius.[object_id]) AS [Table Name] ,
i.name AS [Index Name] ,
i.index_id ,
user_updates AS [Total Writes] ,
user_seeks + user_scans + user_lookups AS [Total Reads] ,
user_updates - ( user_seeks + user_scans + user_lookups ) AS [Difference]
FROM sys.dm_db_index_usage_stats AS ddius WITH ( NOLOCK )
INNER JOIN sys.indexes AS i WITH ( NOLOCK ) ON ddius.[object_id] = i.[object_id] AND i.index_id = ddius.index_id
WHERE OBJECTPROPERTY(ddius.[object_id], 'IsUserTable') = 1
AND ddius.database_id = DB_ID()
AND user_updates > ( user_seeks + user_scans + user_lookups )
AND i.index_id > 1
ORDER BY [Difference] DESC ,
[Total Writes] DESC ,
[Total Reads] ASC ;

Pg. 7

O contedo deste documento confidencial

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.

Determinar padres de utilizao dos ndices


(index_operational_stats)
A sys.dm_db_index_operational_stats uma DMF; aceita database_id , object_id, index_id, e partition_number como
parmetros, para identificar o objeto (heap, ndice clustered ou no clustered) em questo, e devolve as estatsticas
operacionais detalhadas para cada partio desse objeto. Disponibiliza estatsticas de utilizao dos ndices mais
detalhadas que as disponibilizadas pela DMV sys.dm_db_index_usage_stats, bem como sinais de potenciais bloqueios (locks
a pginas, etc.) ou conteno nos objetos (latch) ou excesso de I/O. Todos os parmetros podem ser NULL ou DEFAULT se
pretendermos obter todos os resultados.
Os dados desta DMF so cumulativos, e so reiniciados quando o servidor reiniciado ou quando o ndice removido e
recriado. As estatsticas mantm-se quando o ndice reconstruido, reorganizado e at quando desativado e
reconstrudo.
Enquanto um ndice aparece sempre na DMV index_usage_stats desde que tenha sido utilizado, os dados devolvidos pela
DMF index_operational_stats so de natureza mais transitria, como referido pelos Books Online:
Os dados devolvidos pela sys.dm_db_index_operational_stats s existem enquanto o objeto da cache de meta-dados
que representa o heap ou o ndice est disponvel Portanto, um heap ou ndice ativo provavelmente ter sempre os seus
meta-dados na cache e as contagens cumulativas podero refletir a atividade desde que a instncia do SQL Server foi
iniciada pela ltima vez. Os meta-dados de um heap ou ndice menos ativo so constantemente inseridos e removidos da
cache medida que ele usado. Como resultado, ele pode ou no ter valores disponveis
Uma vez que a unidade granular da funo o nvel da partio, uma tabela que tenha 5 parties ter 5 linhas na DMF,
enquanto na sys.dm_db_index_usage_stats somente ir aparecer uma linha. Deve-se usar as estatsticas de utilizao se
queremos contadores sobre as utilizaes (cada utilizao contabilizada uma vez).
Enquanto as estatsticas de utilizao do uma noo de como o ndice usado pelo optimizador para responder a certas
pesquisas, as estatsticas operacionais devolvem uma informao mais detalhada de como o ndice usado a um nvel
fsico, atravs de colunas como leaf_insert_count, leaf_update_count e leaf_delete_count (o nmero acumulado de
operaes a nvel das folhas de dados), bem como as colunas nonleaf_* equivalentes (para modificaes a acima do nvel
das folhas).
Para diagnosticas conteno de recursos, as seguintes colunas so baste uteis:

row_lock_count nmero de locks a linhas do ndice


row_lock_wait_count nmero de vezes que as sesses esperaram por locks a linhas
row_lock_wait_in_ms total de tempo que as sesses esperaram por locks a linhas
page_lock_count, page_lock_wait_count, page_lock_wait_in_ms idntico a row_lock_* mas relativo a pginas
index_lock_promotion_attempt_count, index_lock_promotion_count nmero de vezes que um lock para
uma operao tentou ou foi escalado

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)

page_io_latch_wait_count, page_io_latch_wait_in_ms nmero de vezes e tempo de espera enquanto o SQL


carrega pginas do disco para a memria

Esta DMF tem mais colunas interessantes como por exemplo as lob_* que do valores relativos a objetos LOB.

Pg. 8

O contedo deste documento confidencial

Estratgias de indexao

Informao detalhada sobre ndices no usados para leituras


A seguinte consulta devolve somente os ndices que no esto a ser usados para leitura e disponibiliza informao
detalhada sobre o tipo de escritas que so feitos, atravs das colunas leaf_*_count e nonleaf_*_count. Desta forma
podemos ter uma melhor noo de como os ndices esto a ser usados e qual o seu custo.
SELECT '[' + DB_NAME() + '].[' + su.[name] + '].[' + o.[name] + ']' AS [statement] ,
i.[name] AS [index_name] ,
ddius.[user_seeks] + ddius.[user_scans] + ddius.[user_lookups] AS [user_reads] ,
ddius.[user_updates] AS [user_writes] ,
ddios.[leaf_insert_count] ,
ddios.[leaf_delete_count] ,
ddios.[leaf_update_count] ,
ddios.[nonleaf_insert_count] ,
ddios.[nonleaf_delete_count] ,
ddios.[nonleaf_update_count]
FROM sys.dm_db_index_usage_stats ddius
INNER JOIN sys.indexes i ON ddius.[object_id] = i.[object_id] AND i.[index_id] = ddius.[index_id]
INNER JOIN sys.partitions SP ON ddius.[object_id] = SP.[object_id] AND SP.[index_id] = ddius.[index_id]
INNER JOIN sys.objects o ON ddius.[object_id] = o.[object_id]
INNER JOIN sys.sysusers su ON o.[schema_id] = su.[UID]
INNER JOIN sys.[dm_db_index_operational_stats](DB_ID(), NULL, NULL, NULL)
AS ddios
ON ddius.[index_id] = ddios.[index_id]
AND ddius.[object_id] = ddios.[object_id]
AND SP.[partition_number] = ddios.[partition_number]
AND ddius.[database_id] = ddios.[database_id]
WHERE OBJECTPROPERTY(ddius.[object_id], 'IsUserTable') = 1
AND ddius.[index_id] > 0
AND ddius.[user_seeks] + ddius.[user_scans] + ddius.[user_lookups] = 0
ORDER BY ddius.[user_updates] DESC ,
su.[name] ,
o.[name] ,
i.[name ]

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

O contedo deste documento confidencial

Estratgias de indexao

Identificar lock e block a nvel da linha


Tambm se pode obter informao sobre lock e block da sys.dm_db_index_operational_stats.
SELECT '[' + DB_NAME(ddios.[database_id]) + '].[' + su.[name] + '].[' + o.[name] + ']' AS [statement] ,
i.[name] AS 'index_name' ,
ddios.[partition_number] ,
ddios.[row_lock_count] ,
ddios.[row_lock_wait_count] ,
CAST (100.0 * ddios.[row_lock_wait_count] / ( ddios.[row_lock_count] ) AS DECIMAL(5, 2)) AS [%_times_blocked] ,
ddios.[row_lock_wait_in_ms] ,
CAST (1.0 * ddios.[row_lock_wait_in_ms] / ddios.[row_lock_wait_count] AS DECIMAL(15, 2)) AS [avg_row_lock_wait_in_ms]
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) ddios
INNER JOIN sys.indexes i ON ddios.[object_id] = i.[object_id] AND i.[index_id] = ddios.[index_id]
INNER JOIN sys.objects o ON ddios.[object_id] = o.[object_id]
INNER JOIN sys.sysusers su ON o.[schema_id] = su.[UID]
WHERE ddios.row_lock_wait_count > 0
AND OBJECTPROPERTY(ddios.[object_id], 'IsUserTable') = 1
AND i.[index_id] > 0
ORDER BY ddios.[row_lock_wait_count] DESC ,
su.[name] ,
o.[name] ,
i.[name ]

Identificar bloqueios de conteno (latch)


A seguinte consulta devolve os ndices que esto a encontrar mais bloqueios de conteno atravs das colunas
page_io_latch_wait_count e page_io_wait_in_ms.
SELECT '[' + DB_NAME() + '].[' + OBJECT_SCHEMA_NAME(ddios.[object_id])
+ '].[' + OBJECT_NAME(ddios.[object_id]) + ']' AS [object_name] ,
i.[name] AS index_name ,
ddios.page_io_latch_wait_count ,
ddios.page_io_latch_wait_in_ms ,
( ddios.page_io_latch_wait_in_ms / ddios.page_io_latch_wait_count ) AS avg_page_io_latch_wait_in_ms
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) ddios
INNER JOIN sys.indexes i ON ddios.[object_id] = i.[object_id] AND i.index_id = ddios.index_id
WHERE ddios.page_io_latch_wait_count > 0 AND OBJECTPROPERTY(i.OBJECT_ID, 'IsUserTable') = 1
ORDER BY ddios.page_io_latch_wait_count DESC , avg_page_io_latch_wait_in_ms DESC

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

O contedo deste documento confidencial

Estratgias de indexao

Identificar escalonamentos de bloqueios


O SQL Server pode tentar escalar bloqueios devido necessidade de reduzir o nmero total de bloqueios detidos e
memria necessria para manter e geri-los. Por exemplo, bloqueios individuais de linhas podem ser escalados a
bloqueio de tabela. Embora signifique que o SQL Server tem menos trabalho a gerir os bloqueios tambm significa
que h menos concorrncia. Se os processos que esto a executar no servidor esto a causar escalonamento de
bloqueios, convm averiguar a causa disso e se o escalonamento justificado, ou se podemos melhorar os nossos
objetos para evitar isso.
A DMV sys.dm_db_index_operational_stats pode ser consultada por forma a devolver informao sobre o nmero de
tentativas feito pelo SQL Server para escalar bloqueios de linhas e pginas para bloqueios de tabelas de um
determinado objeto.
SELECT OBJECT_NAME(ddios.[object_id], ddios.database_id) AS [object_name] ,
i.name AS index_name ,
ddios.index_id ,
ddios.partition_number ,
ddios.index_lock_promotion_attempt_count ,
ddios.index_lock_promotion_count ,
( ddios.index_lock_promotion_attempt_count / ddios.index_lock_promotion_count ) AS percent_success
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) ddios
INNER JOIN sys.indexes i ON ddios.OBJECT_ID = i.OBJECT_ID AND ddios.index_id = i.index_id
WHERE ddios.index_lock_promotion_count > 0

Identificar ndices associadas a conteno de bloqueio


A DMV sys.dm_os_wait_stats um grande recurso para investigar questes como A base de dados est lenta. Se o
resultado das consultas sys.dm_os_wait_stats apontam para problemas de bloqueio (latch, deadlock, ), o resultado
da consulta que se segue dever ser analisado.
SELECT OBJECT_NAME(ddios.OBJECT_ID, ddios.database_id) AS OBJECT_NAME ,
i.name AS index_name ,
ddios.index_id ,
ddios.partition_number ,
ddios.page_lock_wait_count ,
ddios.page_lock_wait_in_ms ,
CASE WHEN DDMID.database_id IS NULL THEN 'N' ELSE 'Y' END AS missing_index_identified
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) ddios
INNER JOIN sys.indexes i ON ddios.OBJECT_ID = i.OBJECT_ID AND ddios.index_id = i.index_id
LEFT OUTER JOIN ( SELECT DISTINCT database_id , OBJECT_ID FROM sys.dm_db_missing_index_details)
AS DDMID ON DDMID.database_id = ddios.database_id AND DDMID.OBJECT_ID = ddios.OBJECT_ID
WHERE ddios.page_lock_wait_in_ms > 0
ORDER BY ddios.page_lock_wait_count DESC ;

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

O contedo deste documento confidencial

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_details a DMV que disponibiliza informao detalhada relativamente aos ndices


que o optimizador teria escolhido, caso existissem

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

sys.dm_db_missing_index_group_stats a DMV que devolve informao detalhada sobre as mtricas de grupos


dos ndices em falta

sys.dm_db_missing_index_groups a DMV que fornece detalhes de ndices em falta de um grupo especfico,

A seguinte figura mostra a relao entre missing_index_details e index_group_stats, atravs da missing_index_groups.

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.

Detalhes dos ndices em falta


A DMV sys.dm_db_missing_index_details, que
index_handle, object_id e database_id, alm de:

Pg. 12

identifica

os

ndices

em

falta,

devolve

as

colunas

equality_columns colunas uteis baseadas no predicado de igualdade


inequality_columns colunas uteis baseadas no predicado de desigualdade (qualquer comparao que no
=)
included_columns colunas, que includas, sero teis para cobrir a pesquisa
statement nome qualificado da base de dados e objeto identificado por database_id e object_id

O contedo deste documento confidencial

Estratgias de indexao

Colunas dos ndices em falta


A
DMF
sys.dm_db_missing_index_columns recebe
o
parmetro
index_handle (obtido
de
sys.dm_db_missing_index_details ou sys.dm_db_missing_index_group) e devolve uma tabela com um registo para cada
uma das colunas que fazem parte do ndice em falta. Isto permite analisar melhor as colunas dos ndices a criar
(verificar o seu tamanho e tipo de dados). Devolve 3 colunas: column_id, column_name e column_usage, esta ltima
tem os valores de EQUALITY, INEQUALITY ou INCLUDE (que so autoexplicativos).
De notar que as colunas na listagem no esto ordenadas por forma a refletir a ordenao tima para a chave do
ndice, da ser necessrio fazer alguns ajustes adicionais para obter o melhor resultado.

Grupos dos ndices em falta


A DMV sys.dm_db_missing_index_groups simplesmente serve para resolver a relao de muitos para muitos entre
sys.dm_db_missing_index_details e sys.dm_db_missing_index_group_stats. Relaciona um ndice em falta com o seu grupo:

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

index_handle o identificador do ndice, usado para relacionar a linha com sys.dm_db_missing_index_details e


sys.dm_db_missing_index_columns

Estatsticas dos grupos dos ndices em falta


A DMV sys.dm_db_missing_index_group_stats devolve todas as estatsticas relativas ao beneficio que o SQL Server pensa
obter com o ndice em falta, incluindo quantas vezes teria sido usado em seeks e scans e quantos planos de
compilao o usariam.
Devolve as seguintes colunas com informao estatstica:

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

O contedo deste documento confidencial

Estratgias de indexao

Limitaes dos DMOs de ndices em falta


Enquanto estes DMOs so muito uteis para otimizar a estratgia de indexao e melhorar os tempos de execuo das
pesquisas, a informao disponibilizada deve ser usada com cautela. Tal como foi aconselhado anteriormente no de
devem remover ndices sem antes fazer uma anlise profunda do seu impacto, tambm no se devem criar todos os
ndices que estes DMOs sugerem. Isto especialmente verdade em ambientes OLTP, onde demasiados ndices podem
to prejudiciais como poucos ndices.
Como tal necessrio analisar os resultados das consultas e manualmente retirar os resultados que no fazem parte
de uma carga normal.
Outro aviso, o facto destes DMOs dos ndices em falta no apresentarem as colunas na ordem exata para a chave do
ndice, por vezes pode coincidir mas nem sempre, pelo que necessrio analisar bem a ordem das mesmas. Alm
disso, estes DMOs podem sugerir colunas INCLUDE em excesso. So bons para encontrar grandes buracos na
estratgia de indexao mas no devem ser usados como uma ferramenta 100% de tuning.
Adicionalmente, nos Books Online esto registadas as seguintes limitaes:

no possvel obter estatsticas para mais de 500 grupos de ndices em falta


devolve custos menos precisos para pesquisas que tenham s predicados de desigualdade
indica somente colunas INCLUDE para algumas pesquisas, pelo que chaves dos ndices devem ser
selecionadas manualmente
devolve informao simples sobre as colunas onde podero faltar ndices
pode devolver custos diferentes para o mesmo grupo de ndices caso aparea em mltiplos planos de
execuo XML

Encontrar os ndices em falta mais vantajosos


Como que usamos ento corretamente estes DMOs? O objetivo claramente obter uma lista de ndices em falta,
com os mais uteis no topo. A equipa da Microsoft SQL Server Query Optimization props a seguinte formula para
calcular o benefcio total dos ndices sugeridos:
(user_seeks + user_scans) * avg_total_user_cost * (avg_user_impact * 0.01)

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

O contedo deste documento confidencial

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

O contedo deste documento confidencial