sql >> Database >  >> RDS >> Oracle

Constante-tijdindex voor stringkolom op Oracle-database

Hashclusters kunnen O(1)-toegangstijd bieden, maar niet O(1)-beperkingstijd. In de praktijk is de constante toegangstijd van een hashcluster echter slechter dan de O(log N) toegangstijd van een reguliere b-tree-index. Clusters zijn ook moeilijker te configureren en schalen niet goed voor sommige bewerkingen.

Hashcluster maken

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

Maak een normale tabel (ter vergelijking)

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

Traceervoorbeeld

SQL*Plus Autotrace is een snelle manier om het plan uit te leggen en I/O-activiteit per instructie bij te houden. Het aantal I/O-verzoeken wordt aangeduid als "consistente krijgt" en is een goede manier om de hoeveelheid werk te meten. Deze code laat zien hoe de nummers zijn gegenereerd voor andere secties. De zoekopdrachten moeten vaak meer dan één keer worden uitgevoerd om de boel op te warmen.

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>

Vind optimale hashkeys, compromissen

Voor optimale leesprestaties moeten alle hash-botsingen in één blok passen (alle Oracle I/O wordt per blok gedaan, meestal 8K). Het verkrijgen van de ideale opslag is lastig en vereist kennis van het hash-algoritme, de opslaggrootte (niet hetzelfde als de blokgrootte) en het aantal hash-sleutels (de buckets). Oracle heeft een standaardalgoritme en -grootte, dus het is mogelijk om te focussen op slechts één attribuut, het aantal hash-sleutels.

Meer hash-sleutels leiden tot minder botsingen. Dit is goed voor de HASH-prestaties van TABLE ACCESS, omdat er maar één blok hoeft te worden gelezen. Hieronder vindt u het aantal consistente geten voor verschillende hashkey-formaten. Ter vergelijking is ook een indextoegang opgenomen. Met voldoende hashkeys neemt het aantal blokken af ​​tot het optimale aantal, 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

Meer hash-sleutels leiden ook tot meer buckets, meer verspilde ruimte en een langzamere TABLE ACCESS FULL-bewerking.

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

Om mijn resultaten te reproduceren, gebruik je een voorbeeldquery zoals select * from orders_cluster where merchantid = 100001 and transactionid = '1'; en verander de laatste waarde in 1, 20, 300, 4000 en 50000.

Prestatievergelijking

Consistente resultaten zijn voorspelbaar en gemakkelijk te meten, maar uiteindelijk is alleen de tijd van de wandklok van belang. Verrassend genoeg is de indextoegang met 4 keer meer consistente resultaten nog steeds sneller dan het optimale hashclusterscenario.

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

Ik heb de test ook geprobeerd met variabele predikaten, maar de resultaten waren vergelijkbaar.

Schaalt het?

Nee, hashclusters schalen niet. Ondanks de O(1) tijdcomplexiteit van TABLE ACCESS HASH en de O(log n) tijdcomplexiteit van INDEX UNIQUE SCAN, lijken hashclusters nooit beter te presteren dan b-tree-indexen.

Ik heb de bovenstaande voorbeeldcode geprobeerd met 10 miljoen rijen. Het hashcluster was pijnlijk traag om te laden en presteerde nog steeds onder de index op SELECT-prestaties. Ik heb geprobeerd het op te schalen naar 100 miljoen rijen, maar het invoegen zou 11 dagen duren.

Het goede nieuws is dat b*trees goed schalen. Het toevoegen van 100 miljoen rijen aan het bovenstaande voorbeeld vereist slechts 3 niveaus in de index. Ik heb alle DBA_INDEXES . bekeken voor een grote database-omgeving (honderden databases en een petabyte aan gegevens) - de slechtste index had slechts 7 niveaus. En dat was een pathologische index op VARCHAR2(4000) kolommen. In de meeste gevallen blijven uw b-tree-indexen ondiep, ongeacht de tafelgrootte.

In dit geval verslaat O(log n) O(1).

Maar WAAROM?

Slechte hashclusterprestaties zijn misschien het slachtoffer van Oracle's poging om dingen te vereenvoudigen en het soort details te verbergen dat nodig is om een ​​hashcluster goed te laten werken. Clusters zijn moeilijk in te stellen en op de juiste manier te gebruiken en zouden sowieso zelden een significant voordeel opleveren. Oracle heeft er de afgelopen decennia niet veel moeite voor gedaan.

De commentatoren hebben gelijk dat een eenvoudige b-tree-index het beste is. Maar het is niet duidelijk waarom dat waar zou moeten zijn en het is goed om na te denken over de algoritmen die in de database worden gebruikt.




  1. Hebreeuwse tekst invoegen in MySQL met behulp van PHP (vuilnistekst)

  2. MySQL - mysqldump --routines om slechts 1 opgeslagen procedure (op naam) te exporteren en niet elke routine

  3. Hoe pak je een willekeurig item uit een database in Django/postgreSQL?

  4. mySQL:Subquery naar array?