quarta-feira, 20 de julho de 2016

Indexação e Fragmentação (SQLServer)

Índices

Hoje, abordarei sobre um tema que para muitos parece ser um mito e algo temoroso. Contudo, tentarei abordar de forma simples
Não é difícil nos depararmos com estruturas de tabelas mal definidas, com tipos de dados que podem ser melhor definidos, tabelas sem índices e consultas SQLs mal escritas.
Um bom time de DBAs tem como uma de suas premissas a execução de procedimentos rotineiras, a maioria deles agendado, para uma boa saúde do banco de dados. Um destes procedimentos é a indexação/desfragmentação de índices.
Os índices são utilizados para potencializar as operações de buscas em banco de dados. Índices bem definidos podem melhorar de forma exponencial o acesso aos dados gravados em disco, reduzindo drasticamente a varredura desnecessária dentro do disco e da estrutura de arquivos e páginas dos blocos de índice.
Recentemente, fui solicitado em uma consultoria para melhorar o desempenho de um GED.

Vamos lá!

As atividades foram executadas em um servidor com Microsoft SQL Server 2008 R2.

Procedimentos

Resolvi analisar o percentual de fragmentação das tabelas.
Observei diversos índices com um alto percentual de fragmentação. Para analisar a fragmentação criei uma stored procedure para auxiliar nos dados, conforme estrutura a seguir:

Stored Procedure utilizada para analisar o percentual de fragmentação:


CREATE PROCEDURE SP_ESTATISTICA_FRAGMENTACAO
AS
BEGIN
                CREATE TABLE #fragmentacao(
                               object_id integer,
                               tabela VARCHAR(128),
                               index_id integer,
                               nome_indice VARCHAR(500),
                               percentual_fragmentacao numeric(18,6))

                DECLARE @Tabela VARCHAR(128)
                DECLARE @catalogo VARCHAR(128)

                DECLARE Tabelas CURSOR FAST_FORWARD FOR
                               SELECT TABLE_CATALOG, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'
                OPEN Tabelas
                FETCH NEXT FROM Tabelas INTO @catalogo, @Tabela

                WHILE @@FETCH_STATUS = 0
                BEGIN
                               INSERT INTO #fragmentacao
                               SELECT b.object_id, @tabela, a.index_id, b.name nome_indice, avg_fragmentation_in_percent
              FROM sys.dm_db_index_physical_stats (DB_ID(@catalogo), OBJECT_ID(@tabela), NULL, NULL, NULL) AS a 
                               JOIN sys.indexes AS b ON a.object_id = b.object_id AND a.index_id = b.index_id
                              
                               FETCH NEXT FROM Tabelas INTO @catalogo, @Tabela   
                END

                CLOSE Tabelas
                DEALLOCATE Tabelas

       SELECT * FROM #fragmentacao

       DROP TABLE #fragmentacao
END

exec SP_ESTATISTICA_FRAGMENTACAO


Resolvi aprofundar um pouco mais nas consultas e recuperei para cada tabela o espaço consumido por Dados, Índices, Reservado, Não utilizado e o Número de tuplas.
Para isso fiz a seguinte view:

View para análise de dados das tabelas:


CREATE VIEW VW_ESTATISTICA_TABELA
AS
SELECT OBJECT_NAME(object_id) As Tabela, Rows As Linhas,
    SUM(Total_Pages * 8) As Reservado,
    SUM(CASE WHEN Index_ID > 1 THEN 0 ELSE Data_Pages * 8 END) As Dados,
        SUM(Used_Pages * 8) -
        SUM(CASE WHEN Index_ID > 1 THEN 0 ELSE Data_Pages * 8 END) As Indice,
    SUM((Total_Pages - Used_Pages) * 8) As NaoUtilizado
FROM
    sys.partitions As P
    INNER JOIN sys.allocation_units As A ON P.hobt_id = A.container_id
where OBJECT_NAME(object_id) not like 'sys%' and
OBJECT_NAME(object_id) not like 'ifts%' and
OBJECT_NAME(object_id) not like 'queue%' and
OBJECT_NAME(object_id) not like 'fulltext%' and
OBJECT_NAME(object_id) not like 'frag%' and
OBJECT_NAME(object_id) not like 'file%'
GROUP BY OBJECT_NAME(object_id), Rows

SELECT * FROM VW_ESTATISTICA_TABELA ORDER BY linhas desc


Feitas as devidas verificações passei para o passo seguinte que foi gerar os dados do antes da desfragmentação e reindexação das tabelas.
Em algumas tabelas só a desfragmentação não foi suficiente e foi necessária a reindexação.
Veja os códigos utilizados:

-- COMANDO UTILIZADO PARA REINDEXAR A TABELA
DBCC DBREINDEX ('nome_tabela');

-- COMANDO PARA RECONSTRUIR O INDICIE
ALTER INDEX nome_indice ON nome_tabela REBUILD;

-- COMANDO PARA DESFRAGMENTAR TODOS OS INDICES DA TABELA
ALTER INDEX ALL ON ACS_CIDADE REORGANIZE ;  

-- COMANDO PARA DESFRAGMENTAR INDIVIDUALMENTE CADA ÍNDICE
ALTER INDEX idx_acsCidade_idEstado ON ACS_CIDADE REORGANIZE ;




Nenhum comentário:

Postar um comentário