web-dev-qa-db-ja.com

ORA_HASH関数で使用されるアルゴリズムは何ですか?

UUIDで ORA_HASH 関数( documentation )を呼び出すだけのデータベース呼び出しを行うアプリケーションでコードを見つけましたストリング。これを行う理由は、パーティション化にORA_HASHを使用しているように見える別のシステムに対してサービス呼び出しを行うために値が必要なためです。

Oracleはもちろんのこと、実際のデータベースにアクセスできないアプリケーションに対して同様のサービス呼び出しを行うために再実装できるように、ORA_HASHが使用するアルゴリズムを知りたいのですが。これまでのところ、Oracle APIのドキュメントに相当するものを見つけることができました。

非常に明確にする必要があります:ORA_HASHを複製する必要があります。これは、自分の制御範囲外にある別のシステムが使用しているため、統合する必要があるためです。そのシステムで。はい、MD5のようなreally標準アルゴリズムを使用できればいいのですが、それがORA_HASHでカバーされていない限り、できません。

ORA_HASH以外のハッシュアルゴリズムの使用を提案する回答やコメントは役に立ちません。この質問は特にORA_HASHに関するものであり、一般的なハッシュやパーティション化ではありません。

17
Kaypro II

oRA_HASHを使用しているように見える別のシステム

まあ、それが「使用しているように見える」場合は、少しリバースエンジニアリングを行って、正確に何が呼び出されているかを確認し、関数のコードを逆アセンブルすることは理にかなっています。

ただし、Oracleの内部について詳しく知りたい場合は、次の手順が役立ちます。

まず、内部C関数が呼び出されているかを理解する必要があります。そのためには、1つのセッションで長時間実行されるコードを実行できます。私はこれを実行しました

_select avg(ora_hash(rownum)) id from
(select rownum from dual connect by rownum <= 1e4),
(select rownum from dual connect by rownum <= 1e4);
_

PL/SQLコードでもかまいませんが、ora_hashが常に呼び出されるようにする必要があります。

実行中

私はWindowsでテストしましたが、ora_hashは...-> evaopn2()->evahash()-> ...

では、evahashについてグーグルしましょう。公式サイトにヘッダーファイルがあるため、非常に幸運でした https://oss.Oracle.com/projects/ocfs-tools/src/branches/new-dir-format/libocfs/Linux/inc/ocfshash .h evahashへのリンクあり。

そして最後に、実際のCコードのページがあります http://burtleburtle.net/bob/hash/evahash.html

これまでのところ、ライブラリ(Windows上のDLL)にビルドすると、Oracleで外部C関数を使用できることを覚えています。

たとえば、Win x64で関数のシグネチャを

_extern "C" ub4 hash( ub1 *k, ub4 length, ub4 initval)
_

oracleから正常に実行できます。ただし、ご覧のとおり、署名はOracleのora_hashとは少し異なります。この関数は値、その長さ、およびinitval(シードの場合があります)を受け入れますが、Oracleの署名はora_hash(expr、max_bucket、seed_value)です。

テストしてみましょうOracle

_SQL> select ora_hash(utl_raw.cast_to_raw('0'), power(2, 32) - 1, 0) oh1,
  2         ora_hash('0', power(2, 32) - 1, 0) oh2,
  3         ora_hash(0, power(2, 32) - 1, 0) oh3,
  4         ora_hash(chr(0), power(2, 32) - 1, 0) oh4
  5    from dual;

       OH1        OH2        OH3        OH4
---------- ---------- ---------- ----------
3517341953 3517341953 1475158189 4056412421
_

[〜#〜] c [〜#〜]

_int main()
{
    ub1 ta[] = {0};
    ub1* t = ta;
    cout << hash(t, 1, 0) << endl;
    ub1 ta0[] = {'0'};
    ub1* t0 = ta0;
    cout << hash(t0, 1, 0) << endl;
    return 0;
}

1843378377
4052366646
_

一致する番号はありません。それで問題は何ですか? ora_hashはほとんどすべてのタイプのパラメーター(たとえば、select ora_hash(sys.odcinumberlist(1,2,3)) from dual)を受け入れますが、C関数は値をバイトの配列として受け入れます。これは、関数呼び出しの前に一部の変換が行われることを意味します。したがって、前述のCハッシュ関数を使用する前に、実際の値がそれに渡される前にどのように変換されるかを理解する必要があります。

IDA PRO + 16進光線を使用してOracleバイナリのリバースエンジニアリングを続行できますが、これには数日かかる場合があります。プラットフォーム固有の詳細は言うまでもありません。

したがって、ora_hashを模倣する場合、最も簡単なオプションは、Oracle Express Editionをインストールし、それを使用してora_hashを呼び出すことです。

面白かったと思います。幸運を。

更新

ora_hashとdbms_utility.get_hash_valueは相互にマッピングできます( https://jonathanlewis.wordpress.com/2009/11/21/ora_hash-function/ を参照)

_SQL> select dbms_utility.get_hash_value('0', 0 + 1, 1e6 + 1) ha1,
  2         ora_hash('0', 1e6, 0) + 1 ha2
  3    from dual;

       HA1        HA2
---------- ----------
    338437     338437
_

Dbms_utilityのパッケージ本体をアンラップすると、次の宣言が表示されます

_  function get_hash_value(name varchar2, base number, hash_size number)
    return number is
  begin
    return(icd_hash(name, base, hash_size));
  end;
_

そして

_  function icd_hash(name      varchar2,
                    base      binary_integer,
                    hash_size binary_integer) return binary_integer;
  pragma interface(c, icd_hash);
_

_icd_hash_を検索してみますと、__psdhsh_( https://yurichev.com/blog/50/ )にマッピングされていることがわかります。次に、Oracle.exeを逆アセンブルして、そこから__psdhsh_のコードを抽出します。たぶん、来年はこれに少し時間をかけるでしょう。

20
Dr Y Wit

これはora_hashの背後にある実際のアルゴのOPの質問には答えません。これは、pl/sqlでのora_hashの使用例です(@JonHellerコメントに回答)。

関数:

SQL> create or replace function get_ora_hash(i_str in varchar2, i_max_bucket in number default 4294967295, i_seed number default 0)
return number deterministic
parallel_enable
as
  rv number:= 0;
begin

select ORA_HASH(i_str, i_max_bucket, i_seed) 
into rv 
from dual;

return rv;

end;
Function created.

そしてそれを使う:

SQL> declare
  l_val number;
begin
  l_val := get_ora_hash('test');
  dbms_output.put_line(l_val);
end;
 PL/SQL procedure successfully completed.

Dbms出力:

2662839991

RESULT_CACHEやその他の手法をいじって、さらに高速化することもできます。

すでに非常に高速です。たとえば、大きなテーブルで関数を100万回呼び出します。

SQL> set serveroutput on
SQL> declare
  l_val number;
  l_start_dte timestamp;
  l_end_dte timestamp;
  l_interval INTERVAL DAY(9) TO SECOND(9);
  l_cnt number := 0;
begin
  l_start_dte:= systimestamp;
  --for rec in (select object_name from dba_objects)
  for rec in (select name from my_big_table where rownum <= 1000000)
  loop
    l_cnt := l_cnt + 1;
    l_val := get_ora_hash(rec.name);
  end loop;
  l_end_dte:= systimestamp;
  l_interval := l_end_dte - l_start_dte;
  dbms_output.put_line('Rows processed: ' || l_cnt 
    || ', Start: ' || l_start_dte  
    || ', End: ' || l_end_dte 
    || ', Interval: ' || l_interval);
end;
Rows processed: 1000000, Start: 14-DEC-17 02.48.31.138212 PM, End: 14-DEC-17 02.48.41.148884 PM, Interval: +000000000 00:00:10.010672000
 PL/SQL procedure successfully completed.

したがって、基本的に1秒あたり10万行です。これには、懸念されるコンテキストスイッチが含まれます。

パフォーマンスのためにORA_HASHを再現する必要がある場合は、パフォーマンスのボトルネックが別の場所にある可能性があることをお勧めします。

1
tbone