ORA_HASH函数使用的算法是什么?

时间:2017-08-29 21:01:49

标签: oracle algorithm hash plsql database-partitioning

我在我正在处理的应用程序中遇到了一些代码,这些代码只是在UUID字符串上调用ORA_HASH函数(documentation)。它这样做的原因是它需要值来对另一个似乎使用ORA_HASH进行分区的系统进行服务调用。

我想知道算法ORA_HASH使用,以便我可以重新实现它,以便为无法访问真实数据库的应用程序进行类似的服务调用,更不用说Oracle了。到目前为止,我只能找到相当于Oracle API文档的内容。

只是要非常明确:我需要克隆ORA_HASH,因为这是我控制之外的另一个系统所使用的,我需要整合用那个系统。是的,如果可以使用真正的标准算法(如MD5),那就太好了,但我不能,除非那是ORA_HASH所涵盖的内容。

建议使用除ORA_HASH之外的散列算法的答案或评论没有帮助。这个问题具体是关于ORA_HASH,而不是一般的散列或分区。

2 个答案:

答案 0 :(得分:19)

  

另一个似乎使用ORA_HASH的系统

好吧,如果它“似乎正在使用”,那么进行一些逆向工程并检查究竟是什么调用并拆解函数代码是有意义的。

但是,如果您想深入了解Oracle内部,那么可能会有所帮助。

首先,您必须弄清楚内部C函数的调用。 为此,您可以在一个会话中执行一些长时间运行的代码。 我确实运行了这个

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是......-&gt; evaopn2() - &gt; evahash() - &gt; ...

现在让我们谷歌为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略有不同。此函数接受值,其长度和初始值(可能是种子),而Oracle中的签名是ora_hash(expr,max_bucket,seed_value)。

让我们试试吧 的甲骨文

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 + hex ray继续对Oracle二进制文件进行逆向工程,但这可能需要数天时间。更不用说平台特定的细节。

因此,如果您想模仿ora_hash,最简单的选择是安装Oracle Express版本并使用它来调用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,我们可以发现它已映射到_psdhshhttps://yurichev.com/blog/50/)。现在是时候反汇编oracle.exe并从中提取_psdhsh的代码了。也许明年我会花一些时间。

答案 1 :(得分:1)

这并没有回答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.

所以基本上每秒100k行,包括你可能担心的任何上下文切换。

如果由于性能需要重现ORA_HASH,我建议您的性能瓶颈可能在其他地方。