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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ORA_HASH函数使用的算法是什么?相关的知识,希望对你有一定的参考价值。

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

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

只是要非常清楚:我需要克隆ORA_HASH,因为这是我控制之外的另一个系统所使用的,我需要与该系统集成。是的,如果可以使用真正标准的算法,如MD5,那将是很好的,但我不能,除非那是ORA_HASH的内幕。

除了ORA_HASH之外,建议使用散列算法的答案或注释也没有用。这个问题具体是关于ORA_HASH,而不是散列或分区。

答案

另一个似乎使用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是......-> 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略有不同。此函数接受值,其长度和初始值(可能是种子),而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 + 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的代码了。也许明年我会花一些时间。

另一答案

这并没有回答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,我建议您的性能瓶颈可能在其他地方。

以上是关于ORA_HASH函数使用的算法是什么?的主要内容,如果未能解决你的问题,请参考以下文章

如何实现 ora_hash(将任何 sql 数据类型划分为 n 个桶的可种子哈希)

有人可以解释啥是 SVN 平分算法吗?理论上和通过代码片段[重复]

在 Visual Studio 中创建构造函数的代码片段或快捷方式

片段(Java) | 机试题+算法思路+考点+代码解析 2023

Java基础入门五)之方法以及递归算法

一个函数应该返回什么可以失败?