sql >> Database teknologi >  >> RDS >> Oracle

Konstant-tidsindeks for strengkolonne på Oracle-database

Hash-klynger kan give O(1)-adgangstid, men ikke O(1)-begrænsningstid. I praksis er den konstante adgangstid for en hash-klynge imidlertid værre end O(log N)-adgangstiden for et almindeligt b-træindeks. Desuden er klynger sværere at konfigurere og skalerer ikke godt til nogle operationer.

Opret Hash-klynge

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;
/

Opret en almindelig tabel (til sammenligning)

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;
/

Sporingseksempel

SQL*Plus Autotrace er en hurtig måde at finde forklaringsplanen og spore I/O-aktivitet pr. sætning. Antallet af I/O-anmodninger er mærket som "konsistente gets" og er en anstændig måde at måle mængden af ​​udført arbejde. Denne kode viser, hvordan tallene blev genereret for andre sektioner. Forespørgslerne skal ofte køres mere end én gang for at varme tingene op.

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>

Find optimale hashnøgler, afvejninger

For optimal læseydelse bør alle hash-kollisioner passe i én blok (al Oracle I/O udføres pr. blok, normalt 8K). Det er svært at få den ideelle opbevaring rigtigt og kræver at kende hash-algoritmen, lagerstørrelsen (ikke det samme som blokstørrelsen) og antallet af hash-nøgler (bøtterne). Oracle har en standardalgoritme og -størrelse, så det er muligt kun at fokusere på én egenskab, nemlig antallet af hash-nøgler.

Flere hash-nøgler fører til færre kollisioner. Dette er godt for TABLE ACCESS HASH ydeevne, da der kun er én blok at læse. Nedenfor er antallet af ensartede gets for forskellige hashkey-størrelser. Til sammenligning er der også inkluderet en indeksadgang. Med nok hashkeys falder antallet af blokke til det optimale antal, 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

Flere hash-nøgler fører også til flere spande, mere spildplads og en langsommere BORDADGANG FULD operation.

Table type      Space in MB
HeapTable       24MB
Hashkeys 100    26MB
hashkeys 1000   30MB
hashkeys 10000  81MB

For at reproducere mine resultater skal du bruge en eksempelforespørgsel som select * from orders_cluster where merchantid = 100001 and transactionid = '1'; og ændre den sidste værdi til 1, 20, 300, 4000 og 50000.

Sammenligning af ydeevne

Konsistente gets er forudsigelige og nemme at måle, men i slutningen af ​​dagen er det kun vægurets tid, der betyder noget. Overraskende nok er indeksadgangen med 4 gange mere ensartede gets stadig hurtigere end det optimale hash-klyngescenarie.

--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;
/

Jeg prøvede også testen med variable prædikater, men resultaterne var ens.

Skalerer det?

Nej, hash-klynger skaleres ikke. På trods af O(1)-tidskompleksiteten af ​​TABLE ACCESS HASH og O(log n)-tidskompleksiteten af ​​INDEX UNIQUE SCAN, ser hash-klynger aldrig ud til at overgå b-tree-indekser.

Jeg prøvede ovenstående eksempelkode med 10 millioner rækker. Hash-klyngen var smertefuldt langsom at indlæse og underpræsterede stadig indekset for SELECT-ydeevne. Jeg forsøgte at skalere den op til 100 millioner rækker, men indsættelsen skulle tage 11 dage.

Den gode nyhed er, at b*trees skalerer godt. Tilføjelse af 100 millioner rækker til ovenstående eksempel kræver kun 3 niveauer i indekset. Jeg kiggede på alle DBA_INDEXES for et stort databasemiljø (hundredevis af databaser og en petabyte data) - havde det dårligste indeks kun 7 niveauer. Og det var et patologisk indeks på VARCHAR2(4000) kolonner. I de fleste tilfælde vil dine b-tree-indekser forblive lavvandede uanset tabellens størrelse.

I dette tilfælde slår O(log n) O(1).

Men HVORFOR?

Dårlig hash-klynge-ydelse er måske et offer for Oracles forsøg på at forenkle tingene og skjule den slags detaljer, der er nødvendige for at få en hash-klynge til at fungere godt. Klynger er svære at konfigurere og bruge korrekt og vil sjældent give en væsentlig fordel alligevel. Oracle har ikke lagt en stor indsats i dem i de sidste par årtier.

Kommentatorerne har ret i, at et simpelt b-træindeks er bedst. Men det er ikke indlysende, hvorfor det skulle være sandt, og det er godt at tænke på de algoritmer, der bruges i databasen.




  1. Persisk karakters problem i mysql-databasen

  2. Opret forbindelse til SQL Server via IP-adresse

  3. Opdater en anden tabel efter indsættelse ved hjælp af en trigger?

  4. Oracle 10g accepterer 5-cifret årstal i en Dato