ハッシュクラスターはO(1)アクセス時間を提供できますが、O(1)制約施行時間は提供できません。ただし、実際には、ハッシュクラスターの一定のアクセス時間は、通常のbツリーインデックスのO(log N)アクセス時間よりも遅くなります。また、クラスターは構成がより難しく、一部の操作では適切に拡張できません。
ハッシュクラスターの作成
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;
/
通常のテーブルを作成する(比較用)
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;
/
トレースの例
SQL * Plus Autotraceは、Explain Planを検索し、ステートメントごとのI/Oアクティビティーを追跡するための迅速な方法です。 I / O要求の数は「一貫性のある取得」とラベル付けされており、実行された作業量を測定するための適切な方法です。このコードは、他のセクションの番号がどのように生成されたかを示しています。多くの場合、ウォームアップのためにクエリを複数回実行する必要があります。
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>
最適なハッシュキー、トレードオフを見つける
最適な読み取りパフォーマンスを得るには、すべてのハッシュ衝突が1つのブロックに収まる必要があります(すべてのOracle I / Oはブロックごとに実行され、通常は8Kです)。理想的なストレージを正しく取得するには注意が必要であり、ハッシュアルゴリズム、ストレージサイズ(ブロックサイズとは異なります)、およびハッシュキー(バケット)の数を知っている必要があります。 Oracleにはデフォルトのアルゴリズムとサイズがあるため、ハッシュキーの数という1つの属性のみに焦点を当てることができます。
ハッシュキーが多いほど、衝突が少なくなります。読み取るブロックが1つしかないため、これはTABLEACCESSHASHのパフォーマンスに適しています。以下は、さまざまなハッシュキーサイズの一貫した取得の数です。比較のために、インデックスアクセスも含まれています。十分な数のハッシュキーがあると、ブロック数は最適な数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
ハッシュキーが増えると、バケットが増え、スペースが無駄になり、TABLEACCESSFULL操作が遅くなります。
Table type Space in MB
HeapTable 24MB
Hashkeys 100 26MB
hashkeys 1000 30MB
hashkeys 10000 81MB
結果を再現するには、select * from orders_cluster where merchantid = 100001 and transactionid = '1';
最後の値を1、20、300、4000、および50000に変更します。
パフォーマンスの比較
一貫性のある取得は予測可能で測定が簡単ですが、結局のところ、重要なのは実時間だけです。驚いたことに、4倍の一貫性のある取得を使用したインデックスアクセスは、最適なハッシュクラスターシナリオよりも高速です。
--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;
/
可変述語を使用したテストも試しましたが、結果は同様でした。
スケーリングしますか?
いいえ、ハッシュクラスターはスケーリングしません。 TABLE ACCESS HASHのO(1)時間計算量、およびINDEX UNIQUE SCANのO(log n)時間計算量にもかかわらず、ハッシュクラスターがbツリーインデックスよりも優れているようには見えません。
上記のサンプルコードを1,000万行で試しました。ハッシュクラスターの読み込みは非常に遅く、SELECTパフォーマンスのインデックスのパフォーマンスは依然として低かった。 1億行に拡大しようとしましたが、挿入には11日かかりました。
良いニュースは、b*treesがうまくスケーリングすることです。上記の例に1億行を追加するには、インデックスに3つのレベルしか必要ありません。すべてのDBA_INDEXES
を見ました 大規模なデータベース環境(数百のデータベースとペタバイトのデータ)の場合、最悪のインデックスには7つのレベルしかありませんでした。そして、それはVARCHAR2(4000)
の病理学的インデックスでした 列。ほとんどの場合、テーブルのサイズに関係なく、Bツリーインデックスは浅いままです。
この場合、O(log n)はO(1)よりも優れています。
しかし、なぜですか?
ハッシュクラスタのパフォーマンスの低下は、ハッシュクラスタを適切に機能させるために必要な詳細を単純化し、隠そうとするOracleの試みの犠牲になっている可能性があります。クラスターを適切にセットアップして使用することは困難であり、とにかく大きなメリットを提供することはめったにありません。オラクルは過去数十年間、彼らに多くの努力を払っていません。
コメント提供者は、単純なbツリーインデックスが最適であると正しいです。しかし、なぜそれが真実である必要があるのかは明らかではなく、データベースで使用されているアルゴリズムについて考えるのは良いことです。