Oracle
 sql >> Baza danych >  >> RDS >> Oracle

Indeks czasu stałego dla kolumny ciągu w bazie danych Oracle

Klastry haszujące mogą zapewniać czas dostępu O(1), ale nie czas wymuszania ograniczeń O(1). Jednak w praktyce stały czas dostępu klastra mieszającego jest gorszy niż czas dostępu O(log N) zwykłego indeksu b-drzewa. Ponadto klastry są trudniejsze do skonfigurowania i nie skalują się dobrze w przypadku niektórych operacji.

Utwórz klaster haszujący

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

Utwórz zwykłą tabelę (do porównania)

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

Przykład śledzenia

SQL*Plus Autotrace to szybki sposób na znalezienie planu wyjaśniania i śledzenie aktywności we/wy na instrukcję. Liczba żądań we/wy jest oznaczona jako „spójne pobieranie” i jest przyzwoitym sposobem mierzenia ilości wykonanej pracy. Ten kod pokazuje, jak wygenerowano liczby dla innych sekcji. Zapytania często muszą być uruchamiane więcej niż raz, aby się rozgrzać.

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>

Znajdź optymalne skróty, kompromisy

Aby uzyskać optymalną wydajność odczytu, wszystkie kolizje skrótów powinny mieścić się w jednym bloku (wszystkie operacje we/wy Oracle są wykonywane na blok, zwykle 8K). Uzyskanie idealnej pamięci masowej jest trudne i wymaga znajomości algorytmu skrótu, rozmiaru pamięci (nie tego samego, co rozmiar bloku) i liczby kluczy skrótu (wiader). Oracle ma domyślny algorytm i rozmiar, dzięki czemu można skupić się tylko na jednym atrybucie, liczbie kluczy haszujących.

Więcej kluczy mieszających prowadzi do mniejszej liczby kolizji. Jest to dobre dla wydajności funkcji TABLE ACCESS HASH, ponieważ do odczytania jest tylko jeden blok. Poniżej znajduje się liczba spójnych pobiera dla różnych rozmiarów hashkey. Dla porównania uwzględniony jest również dostęp do indeksu. Przy wystarczającej liczbie skrótów liczba bloków spada do optymalnej liczby, 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

Więcej kluczy skrótu prowadzi również do większej liczby segmentów, większej ilości zmarnowanego miejsca i wolniejszej operacji TABLE ACCESS FULL.

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

Aby odtworzyć moje wyniki, użyj przykładowego zapytania, takiego jak select * from orders_cluster where merchantid = 100001 and transactionid = '1'; i zmień ostatnią wartość na 1, 20, 300, 4000 i 50000.

Porównanie wydajności

Stałe pobrania są przewidywalne i łatwe do zmierzenia, ale pod koniec dnia liczy się tylko czas zegara ściennego. Co zaskakujące, dostęp do indeksu z 4 razy bardziej konsekwentnym uzyskiwaniem jest nadal szybszy niż optymalny scenariusz klastra mieszającego.

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

Próbowałem również testu ze zmiennymi predykatami, ale wyniki były podobne.

Czy to się skaluje?

Nie, klastry haszujące nie skalują się. Pomimo złożoności czasowej O(1) funkcji TABLE ACCESS HASH i złożoności czasowej O(log n) funkcji INDEX UNIQUE SCAN, klastry haszujące nigdy nie wydają się przewyższać indeksów b-drzewa.

Próbowałem powyższego przykładowego kodu z 10 milionami wierszy. Klaster haszujący ładował się boleśnie wolno i nadal miał gorsze wyniki niż indeks wydajności SELECT. Próbowałem przeskalować go do 100 milionów wierszy, ale wstawienie zajmie 11 dni.

Dobrą wiadomością jest to, że b*trees dobrze się skalują. Dodanie 100 milionów wierszy do powyższego przykładu wymaga tylko 3 poziomów w indeksie. Spojrzałem na wszystkie DBA_INDEXES dla dużego środowiska bazodanowego (setki baz i petabajt danych) - najgorszy indeks miał tylko 7 poziomów. I to był patologiczny indeks na VARCHAR2(4000) kolumny. W większości przypadków indeksy b-drzewa pozostaną płytkie, niezależnie od wielkości stołu.

W tym przypadku O(log n) bije O(1).

Ale DLACZEGO?

Słaba wydajność klastra mieszającego jest prawdopodobnie ofiarą prób Oracle uproszczenia rzeczy i ukrycia rodzaju szczegółów niezbędnych do prawidłowego działania klastra mieszającego. Klastry są trudne do skonfigurowania i prawidłowego użytkowania, a i tak rzadko przynoszą znaczące korzyści. Oracle nie włożyło w nie wiele wysiłku w ciągu ostatnich kilku dekad.

Komentatorzy mają rację, że prosty indeks b-drzewa jest najlepszy. Ale nie jest oczywiste, dlaczego tak jest i dobrze jest pomyśleć o algorytmach używanych w bazie danych.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Oracle SQL :znaczniki czasu w klauzuli gdzie

  2. Liczba rekordów w instrukcji Insert (Oracle)

  3. Zapytanie usuwania nie działa z aplikacji Java

  4. PLSQL:Uzyskaj liczbę rekordów zaktualizowanych w porównaniu z wstawionymi, gdy używana jest instrukcja scalająca

  5. Znalezienie zapytania od Oracle, które blokuje sesję