Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Índice de tempo constante para coluna de string no banco de dados Oracle


Os clusters de hash podem fornecer o tempo de acesso O(1), mas não o tempo de aplicação da restrição O(1). No entanto, na prática, o tempo de acesso constante de um cluster de hash é pior do que o tempo de acesso O(log N) de um índice regular de b-tree. Além disso, os clusters são mais difíceis de configurar e não são bem dimensionados para algumas operações.

Criar cluster de hash
drop table orders_cluster;
drop cluster cluster1;

create cluster cluster1
(
    MerchantID number,
    TransactionID varchar2(20)
)
single table hashkeys 10000; --This number is important, choose wisely!

create table orders_cluster
(
    id number,
    MerchantID number,
    TransactionID varchar2(20)
) cluster cluster1(merchantid, transactionid);

--Add 1 million rows.  20 seconds.
begin
    for i in 1 .. 10 loop
        insert into orders_cluster
        select rownum + i * 100000, mod(level, 100)+ i * 100000, level
        from dual connect by level <= 100000;
        commit;
    end loop;
end;
/

create unique index orders_cluster_idx on orders_cluster(merchantid, transactionid);

begin
    dbms_stats.gather_table_stats(user, 'ORDERS_CLUSTER');
end;
/

Criar tabela normal (para comparação)
drop table orders_table;

create table orders_table
(
    id number,
    MerchantID number,
    TransactionID varchar2(20)
) nologging;

--Add 1 million rows.  2 seconds.
begin
    for i in 1 .. 10 loop
        insert into orders_table
        select rownum + i * 100000, mod(level, 100)+ i * 100000, level
        from dual connect by level <= 100000;
        commit;
    end loop;
end;
/

create unique index orders_table_idx on orders_table(merchantid, transactionid);

begin
    dbms_stats.gather_table_stats(user, 'ORDERS_TABLE');
end;
/

Exemplo de rastreamento

O SQL*Plus Autotrace é uma maneira rápida de encontrar o plano de explicação e rastrear a atividade de E/S por instrução. O número de solicitações de E/S é rotulado como "obtidas consistentes" e é uma maneira decente de medir a quantidade de trabalho realizado. Este código demonstra como os números foram gerados para outras seções. As consultas geralmente precisam ser executadas mais de uma vez para aquecer as coisas.
SQL> set autotrace on;
SQL> select * from orders_cluster where merchantid = 100001 and transactionid = '2';

no rows selected


Execution Plan
----------------------------------------------------------
Plan hash value: 621801084

------------------------------------------------------------------------------------
| Id  | Operation         | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                |     1 |    16 |     1   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS HASH| ORDERS_CLUSTER |     1 |    16 |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("MERCHANTID"=100001 AND "TRANSACTIONID"='2')


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         31  consistent gets
          0  physical reads
          0  redo size
        485  bytes sent via SQL*Net to client
        540  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

SQL>

Encontre Hashkeys e Compensações ideais

Para um desempenho de leitura ideal, todas as colisões de hash devem caber em um bloco (toda a E/S do Oracle é feita por bloco, geralmente 8K). Acertar o armazenamento ideal é complicado e requer conhecer o algoritmo de hash, o tamanho do armazenamento (não o mesmo que o tamanho do bloco) e o número de chaves de hash (os buckets). O Oracle possui um algoritmo e tamanho padrão, portanto, é possível focar em apenas um atributo, o número de chaves de hash.

Mais chaves de hash levam a menos colisões. Isso é bom para o desempenho do TABLE ACCESS HASH, pois há apenas um bloco para ler. Abaixo está o número de obtenções consistentes para diferentes tamanhos de hashkey. Para comparação, um acesso de índice também está incluído. Com hashkeys suficientes, o número de blocos diminui para o número ideal, 1.
Method          Consistent Gets (for transactionid = 1, 20, 300, 4000, and 50000)
Index           4,  3,  3,  3,  3
Hashkeys 100    1, 31, 31, 31, 31
Hashkeys 1000   1,  3,  4,  4,  4
Hashkeys 10000  1,  1,  1,  1,  1

Mais chaves de hash também levam a mais buckets, mais espaço desperdiçado e uma operação TABLE ACCESS FULL mais lenta.
Table type      Space in MB
HeapTable       24MB
Hashkeys 100    26MB
hashkeys 1000   30MB
hashkeys 10000  81MB

Para reproduzir meus resultados, use uma consulta de amostra como select * from orders_cluster where merchantid = 100001 and transactionid = '1'; e altere o último valor para 1, 20, 300, 4000 e 50000.

Comparação de desempenho

Obturações consistentes são previsíveis e fáceis de medir, mas no final do dia apenas a hora do relógio de parede importa. Surpreendentemente, o acesso ao índice com 4 vezes mais obtenções consistentes ainda é mais rápido do que o cenário de cluster de hash ideal.
--3.5 seconds for b-tree access.
declare
    v_count number;
begin
    for i in 1 .. 100000 loop
        select count(*)
        into v_count
        from orders_table
        where merchantid = 100000 and transactionid = '1';
    end loop;
end;
/

--3.8 seconds for hash cluster access.
declare
    v_count number;
begin
    for i in 1 .. 100000 loop
        select count(*)
        into v_count
        from orders_cluster
        where merchantid = 100000 and transactionid = '1';
    end loop;
end;
/

Eu também tentei o teste com predicados variáveis, mas os resultados foram semelhantes.

É dimensionado?

Não, os clusters de hash não são dimensionados. Apesar da complexidade de tempo O(1) do TABLE ACCESS HASH e da complexidade de tempo O(log n) do INDEX UNIQUE SCAN, os clusters de hash nunca parecem superar os índices b-tree.

Eu tentei o código de exemplo acima com 10 milhões de linhas. O cluster de hash foi dolorosamente lento para carregar e ainda apresentou desempenho inferior ao índice no desempenho do SELECT. Tentei escalá-lo para 100 milhões de linhas, mas a inserção levaria 11 dias.

A boa notícia é que as árvores b* escalam bem. Adicionar 100 milhões de linhas ao exemplo acima requer apenas 3 níveis no índice. Eu olhei para todos os DBA_INDEXES para um ambiente de banco de dados grande (centenas de bancos de dados e um petabyte de dados) - o pior índice tinha apenas 7 níveis. E isso era um índice patológico em VARCHAR2(4000) colunas. Na maioria dos casos, seus índices b-tree permanecerão superficiais, independentemente do tamanho da tabela.

Neste caso, O(log n) supera O(1).

Mas POR QUÊ?

O desempenho ruim do cluster de hash talvez seja uma vítima da tentativa da Oracle de simplificar as coisas e ocultar o tipo de detalhes necessários para fazer um cluster de hash funcionar bem. Os clusters são difíceis de configurar e usar corretamente e raramente forneceriam um benefício significativo de qualquer maneira. A Oracle não colocou muito esforço neles nas últimas décadas.

Os comentaristas estão corretos que um índice simples de árvore b é o melhor. Mas não é óbvio por que isso deveria ser verdade e é bom pensar nos algoritmos usados ​​no banco de dados.