Banco                                                 de dados de testes 
Imagine                                               que você tem um banco de                                               dados bem simples do                                               Firebird
CREATE                                               DATABASE                                               “E:\TESTFB25INDEX.FDB”                                               USER “SYSDBA” PASSWORD                                               “masterkey”;
CREATE TABLE                                               TABLEIND1 (
                                                  I1                                                          INTEGER NOT NULL PRIMARY                                               KEY,
                                                  NAME                                                        VARCHAR(250),
                                                  MALEORFEMALE                                                SMALLINT
);
CREATE                                               GENERATOR G1;
SET TERM ^ ;
create or                                               alter procedure INS1MLN
returns (
                                                  INSERTED_CNT integer)
as
BEGIN
inserted_cnt                                               = 0;
WHILE                                               (inserted_cnt <1000000)                                               DO
BEGIN
 Insert                                               into tableind1(i1, name,                                               maleorfemale)                                               values(gen_id(g1,1), 'TEST                                               name', (:inserted_cnt -                                               (:inserted_cnt/2)*2));
 inserted_cnt=inserted_cnt+1;
END
suspend;
END^
SET TERM ; ^
GRANT INSERT                                               ON TABLEIND1 TO PROCEDURE                                               INS1MLN;
GRANT EXECUTE                                               ON PROCEDURE INS1MLN TO                                               SYSDBA;
COMMIT;
Por alguma                                               razão você precisa:
·                                                       inserir                                               1 milhão de registros
·                                                       atualizar                                               todos estes registros
·                                                       apagar                                               todos os registros
·                                                       executar                                               um select count(*) neste                                               banco de dados
Para executar                                               esta tarefa, nós podemos                                               usar o script abaixo (que                                               será executado                                               imediatamente após o                                               script de criação do banco                                               de dados) :
set stat on;                                                /*show statistics*/
select * from                                               ins1mln; 
update                                               tableind1 SET MALEORFEMALE                                               = 3;
delete from                                               tableind1;
select                                               count(*) from tableind1;
guardaremos                                               os resultados para análise                                               posterior.
Então                                               criaremos outro banco de                                               dados com a mesma                                               estrutura, mas                                               adicionaremos um índice na                                               coluna MALEORFEMAIL
CREATE INDEX                                               TABLEIND1_IDX1 ON                                               TABLEIND1 (MALEORFEMALE);                                             
Como                                             pode notar, vamos inserir                                             apenas os valores 0 ou 1                                             nesta coluna.
Repetiremos a                                               execução do script no                                               banco de dados com este                                               índice e compararemos os                                               resultados.
Resultados                                               estão na tabela a seguir:
                                                 Sem                                                         o índice para                                                         MALEORFEMAIL  |                                                  Com                                                         o índice para                                                         MALEORFEMALE  |                                                
                                                 SQL>                                                       set stat on;                                                       /*show                                                       statistics*/ SQL>                                                         select * from                                                         ins1mln;  
 INSERTED_CNT ============                                                            1000000  
 Current                                                       memory = 10487216 Delta                                                       memory = 80560 Max                                                       memory = 12569996 Elapsed                                                       time= 13.33                                                       sec Buffers                                                       = 2048 Reads                                                       = 0 Writes                                                       18756 Fetches                                                       = 7833503 SQL>                                                         update tableind1                                                         SET MALEORFEMALE                                                         = 3; Current                                                       memory = 76551788 Delta                                                       memory = 66064572 Max                                                       memory = 111442520 Elapsed                                                       time= 15.04                                                       sec Buffers                                                       = 2048 Reads                                                       = 16166 Writes                                                       15852 Fetches                                                       = 6032307 SQL>                                                       delete from                                                         tableind1; Current                                                       memory = 76550240 Delta                                                       memory = -1548 Max                                                       memory = 111442520 Elapsed                                                       time= 3.27                                                       sec Buffers                                                       = 2048 Reads                                                       = 16147 Writes                                                       16006 Fetches                                                       = 5032277 SQL>                                                       select count(*)                                                         from tableind1;  
                                                              COUNT ============                                                                  0  
 Current                                                       memory = 76552064 Delta                                                       memory = 1824 Max                                                       memory = 111442520 Elapsed                                                       time= 1.35                                                       sec Buffers                                                       = 2048 Reads                                                       = 16021 Writes                                                       1791 Fetches                                                       = 2032278  |                                                  SQL>                                                       set stat on;                                                       /*show                                                       statistics*/ SQL>                                                       select * from                                                         ins1mln;  
 INSERTED_CNT ============                                                            1000000  
 Current                                                       memory = 10484140 Delta                                                       memory = 75524 Max                                                       memory = 12569996 Elapsed                                                       time= 23.94                                                       sec Buffers                                                       = 2048 Reads                                                       = 1 Writes                                                       23942 Fetches                                                       = 11459599 SQL>                                                       update                                                         tableind1 SET                                                         MALEORFEMALE =                                                         3; Current                                                       memory = 76548712 Delta                                                       memory = 66064572 Max                                                       memory = 111439444 Elapsed                                                       time= 29.30                                                       sec Buffers                                                       = 2048 Reads                                                       = 16167 Writes                                                       19492 Fetches                                                       = 10035948 SQL>                                                       delete from                                                         tableind1; Current                                                       memory = 76547164 Delta                                                       memory = -1548 Max                                                       memory = 111439444 Elapsed                                                       time= 3.41                                                       sec Buffers                                                       = 2048 Reads                                                       = 16147 Writes                                                       15967 Fetches                                                       = 5032277 SQL>                                                       select count(*)                                                         from tableind1;  
                                                              COUNT ============                                                                  0  
 Current                                                       memory = 76548988 Delta                                                       memory = 1824 Max                                                       memory = 111439444 Elapsed                                                       time= 0.69                                                       sec Buffers                                                       = 2048 Reads                                                       = 16021 Writes                                                       1901 Fetches                                                       = 2032278   |                                                
                                           
Portanto,                                               um índice ruim reduz a                                               performance                                                 aproximadamente 2 vezes                                                 durante inserções e                                                 atualizações. Também podemos notar que                                               um índice pouco seletivo                                               aumenta o número de                                               escritas e fetches de                                               registros.
Vamos                                               analisar as estatísticas                                               deste banco de dados de                                               exemplo (com o índice ruim                                               para MALEORFEMALE) e olhar                                               os detalhes.
Para                                               obter as estatísticas,                                               rodaremos o seguinte                                               comando:
gstat -r                                                 e:\testfb25index.fdb                                                 > e:\teststat.txt
A seção                                               para a tabela TABLEIND1 e                                               seus índices tem                                               informações intrigantes,                                               mas que tipo de informação                                               útil nos fornece ?  
TABLEIND1                                               (128)
                                                  Primary pointer page: 166,                                               Index root page: 167
                                                  Average record length:                                               0.00, total records:                                               1000000
                                                  Average version length:                                               27.00, total versions:                                               1000000, max versions: 1
                                                  Data pages: 16130, data                                               page slots: 16130, average                                               fill: 93%
                                                  Fill distribution:
                                                     0 - 19% = 1
                                                    20 - 39% = 0
                                                    40 - 59% = 0
                                                    60 - 79% = 0
                                                    80 - 99% = 16129
                                                  Index RDB$PRIMARY1 (0)
                                                    Depth: 3, leaf buckets:                                               1463, nodes: 1000000
                                                    Average data length: 1.00,                                               total dup: 0, max dup: 0
                                                    Fill distribution:
                                                         0                                               - 19% = 0
                                                        20 -                                               39% = 0
                                                        40 -                                               59% = 0
                                                        60 -                                               79% = 1
                                                        80 -                                               99% = 1462
                                                  Index TABLEIND1_IDX1 (1)
                                                    Depth: 3, leaf buckets:                                               2873, nodes: 2000000
                                                    Average data length: 0.00,                                               total dup: 1999997, max                                               dup: 999999
                                                    Fill distribution:
                                                         0                                               - 19% = 0
                                                        20 -                                               39% = 1
                                                        40 -                                               59% = 1056
                                                        60 -                                               79% = 0
                                                        80 -                                               99% = 1816
Para                                               entender o significado dos                                               números e porcentagens,                                               podemos usar a ferramenta                                                 IBAnalyst, que nos                                               oferece uma interpretação                                               visual das estatísticas do                                               banco de dados:
 
Clicando                                               em "Reports/View                                               recommendations" acharemos                                               a explicação apropriada                                               para este índice:
Bad indices                                               count: 1.
By `bad` we                                               name indices with many                                               duplicate keys (90% of all                                               keys) 
and big                                               groups of equal keys (30%                                               of all keys). Big groups                                               of equal 
keys slowdown                                               garbage collection -                                               MaxEquals here is % of max                                               groups 
of keys                                               having equal values. Index                                               search for such an index                                               is not 
efficient.                                               You can drop such indices                                               (if they are not on FK 
constraints).
Contagem de índices                                               ruins:1.
Chamamos de "Ruins" os                                               índices com muitos valores                                               duplicados
(90% de todas as chaves) e                                               com grupos grandes de                                               chaves iguais
(30% de todas as chaves).                                               Grupos grandes de chaves                                               iguais fazem
com que o processo de                                               garbage collection seja                                               mais lento - MaxEquals
é o % de grupos que contém                                               chaves com o mesmo valor.                                               Pesquisas indexadas
usando tais índices não                                               são eficientes. Você pode                                               remover estes índices
(se eles não forem criados                                               por constraints de FK)
Index                                                        ( Relation)                                                Duplicates  MaxEquals
TABLEIND1_IDX1                                               (TABLEIND1) :                                                 100%,                                                    50%
Em bancos de                                               dados de produção é comum                                               ver muitos índices ruins,                                               o que pode afetar de forma                                               dramática a performance do                                               banco de dados. Neste                                               exemplo vemos uma tabela                                               com 13 milhões de                                               registros que possui 7                                               índices ruins, que são                                               (provavelmente) inúteis e                                               degradam                                               significativamente a                                               performance do Firebird.
Geralmente a                                               primeira ação do                                               desenvolvedor é remover                                               estes índices ruins. Por                                               outro lado, é possível que                                               o índice ruim seja usado                                               em algumas queries                                               especiais, que precisam de                                               uma combinação específica                                               de índices (incluindo o                                               índice ruim) para que                                               possa ser executada de                                               forma suficientemente                                               rápida. 
O Otimizador                                               SQL do Firebird Firebird                                               possui regras complexas, e                                               é comum que removendo o                                               índice ruim faça com que                                               algumas queries tenham a                                               performance                                               significativamente                                               degradada.
Claro que                                               estas queries devem ser                                               reescritas de forma a usar                                               um plano melhor sem o                                               índice ruim, mas ainda                                               temos um problema: como                                               encontrar estas queries                                               que usam o índice ruim                                               ? 
A                                               resposta está no próximo                                                 episódio da série de                                               nossos artigos sobre                                               Otimização.