Cluster hash dapat menyediakan O(1) waktu akses, tetapi tidak O(1) waktu penegakan kendala. Namun, dalam praktiknya, waktu akses konstan dari cluster hash lebih buruk daripada waktu akses O(log N) dari indeks b-tree biasa. Selain itu, cluster lebih sulit untuk dikonfigurasi dan tidak dapat diskalakan dengan baik untuk beberapa operasi.
Buat Cluster 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;
/
Buat Tabel Reguler (Untuk Perbandingan)
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;
/
Contoh Jejak
SQL*Plus Autotrace adalah cara cepat untuk menemukan rencana penjelasan dan melacak aktivitas I/O per pernyataan. Jumlah permintaan I/O diberi label sebagai "consistent get" dan merupakan cara yang layak untuk mengukur jumlah pekerjaan yang dilakukan. Kode ini menunjukkan bagaimana angka dihasilkan untuk bagian lain. Kueri sering kali perlu dijalankan lebih dari sekali untuk menghangatkan suasana.
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>
Temukan Hashkey Optimal, Trade-Off
Untuk kinerja baca yang optimal, semua tabrakan hash harus muat dalam satu blok (semua Oracle I/O dilakukan per blok, biasanya 8K). Mendapatkan hak penyimpanan yang ideal itu rumit dan membutuhkan pengetahuan tentang algoritma hash, ukuran penyimpanan (tidak sama dengan ukuran blok), dan jumlah kunci hash (ember). Oracle memiliki algoritma dan ukuran default sehingga memungkinkan untuk fokus hanya pada satu atribut, jumlah kunci hash.
Lebih banyak kunci hash menyebabkan lebih sedikit tabrakan. Ini bagus untuk kinerja TABLE ACCESS HASH karena hanya ada satu blok untuk dibaca. Di bawah ini adalah jumlah yang konsisten untuk ukuran hashkey yang berbeda. Sebagai perbandingan, akses indeks juga disertakan. Dengan hashkey yang cukup, jumlah blok berkurang ke jumlah optimal, 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
Lebih banyak kunci hash juga menghasilkan lebih banyak ember, lebih banyak ruang yang terbuang, dan operasi PENUH AKSES TABEL yang lebih lambat.
Table type Space in MB
HeapTable 24MB
Hashkeys 100 26MB
hashkeys 1000 30MB
hashkeys 10000 81MB
Untuk mereproduksi hasil saya, gunakan contoh kueri seperti select * from orders_cluster where merchantid = 100001 and transactionid = '1';
dan ubah nilai terakhir menjadi 1, 20, 300, 4000, dan 50000.
Perbandingan Kinerja
Hasil yang konsisten dapat diprediksi dan mudah diukur, tetapi pada akhirnya hanya waktu jam dinding yang penting. Anehnya, akses indeks dengan 4 kali lebih konsisten masih lebih cepat daripada skenario cluster hash yang optimal.
--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;
/
Saya juga mencoba tes dengan predikat variabel tetapi hasilnya serupa.
Apakah Skalanya?
Tidak, kluster hash tidak berskala. Terlepas dari kompleksitas waktu O(1) TABLE ACCESS HASH, dan kompleksitas waktu O(log n) dari INDEX UNIQUE SCAN, cluster hash sepertinya tidak pernah mengungguli indeks b-tree.
Saya mencoba kode sampel di atas dengan 10 juta baris. Cluster hash sangat lambat untuk dimuat, dan masih di bawah kinerja indeks pada kinerja SELECT. Saya mencoba menskalakannya hingga 100 juta baris tetapi penyisipannya akan memakan waktu 11 hari.
Kabar baiknya adalah bahwa b*trees dapat berkembang biak dengan baik. Menambahkan 100 juta baris ke contoh di atas hanya membutuhkan 3 level dalam indeks. Saya melihat semua DBA_INDEXES
untuk lingkungan basis data yang besar (ratusan basis data dan satu petabyte data) - indeks terburuk hanya memiliki 7 level. Dan itu adalah indeks patologis pada VARCHAR2(4000)
kolom. Dalam kebanyakan kasus, indeks b-tree Anda akan tetap dangkal terlepas dari ukuran tabel.
Dalam hal ini, O(log n) mengalahkan O(1).
Tapi MENGAPA?
Kinerja klaster hash yang buruk mungkin merupakan korban dari upaya Oracle untuk menyederhanakan berbagai hal dan menyembunyikan jenis detail yang diperlukan untuk membuat klaster hash berfungsi dengan baik. Cluster sulit diatur dan digunakan dengan benar dan jarang memberikan manfaat yang signifikan. Oracle belum melakukan banyak upaya dalam beberapa dekade terakhir.
Para komentator benar bahwa indeks b-tree sederhana adalah yang terbaik. Tetapi tidak jelas mengapa hal itu harus benar dan ada baiknya untuk memikirkan tentang algoritme yang digunakan dalam database.