2017-08-29 87 views
13

我在應用程序中遇到了一些代碼,這使得數據庫調用僅僅調用UUID字符串上的ORA_HASH函數(documentation)。這樣做的原因在於,它需要將服務呼叫給另一個似乎使用ORA_HASH進行分區的系統。ORA_HASH函數使用的算法是什麼?

我想知道算法ORA_HASH的用法,這樣我就可以重新實現它,爲一個無法訪問真實數據庫的應用程序提供類似的服務調用,更不用說Oracle了。目前爲止,我只能找到相當於Oracle API文檔的內容。

只是要超清晰:我需要克隆ORA_HASH因爲這是另一個系統是我的控制之外使用,我需要與該系統集成。是的,它會很好,如果可以使用真的標準算法,如MD5,但我不能,除非這是什麼ORA_HASH是在掩護下。

ORA_HASH之外的建議使用散列算法的解答或建議無濟於事。這個問題特別是關於ORA_HASH,而不是一般的散列或分區。

+0

如果您想重新實現以調用無法訪問真實數據庫的應用程序,爲什麼需要重新實現'ORA_HASH'?這有什麼神奇的?編寫你自己的簡單散列函數,不要參考'ORA_HASH'。 – mathguy

+0

這裏的關鍵詞是「撥打另一個系統的服務電話」。如果我使用不同的算法,我會得到不同的哈希值,並且調用不起作用。 –

+0

哦......我明白了; 「似乎使用'ORA_HASH'」的「其他系統」不受你控制。我的觀點是,使用你在兩者中創建的相同功能;但如果你只控制一方,我明白你爲什麼需要這個。 – mathguy

回答

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,我會建議您的性能瓶頸可能在其他地方。

+0

我正在使用的遮罩產品已經這樣做了。上下文從「select ora_hash ... from dual」開始切換非常緩慢。如果代碼可以執行'return ora_hash ...',但這是不允許的,那會更快。這就是爲什麼知道ORA_HASH算法可能很有用,因爲我們可能在純PL/SQL中重新創建它。 –

+0

@JonHeller我更新了我的答案。其實非常快。 – tbone

+0

不幸的是,我們的數據屏蔽產品在每一行的每一列都會多次調用ORA_HASH,並且我們有數十億行要處理。因此,替代實施仍然有幫助。 –

12

出現使用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

到目前爲止好,我們記得,我們可以使用外部的C函數在Oracle中,如果我們建立成庫(DLL在Windows上)。

例如我贏X64的,如果我改變函數簽名

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

它可以從Oracle成功執行。 但是,如您所見,簽名有點不同於Oracle中的ora_hash。該函數接受值,它的長度和initval(可能是種子),而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射線對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; 

and

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。也許我會在明年花一些時間。

+0

我相信我可以用它來更快地構建答案。必須有一種你可以傳入的格式不會變化太多。 –

+0

@DylanB去吧。如果花費的時間比賞金時間要長,不要擔心,我會再創造500點獎勵,並將獎勵獎勵給您或任何其他首先構建它的人。 (假設代碼是在PL/SQL中,或者可以從PL/SQL中調用,並匹配ORA_HASH。) –

+0

@Dylan B,我相信如果至少弄清楚它是如何用於varchar2參數的話就足夠了。我玩了一個字節的值,使它儘可能簡單,並避免大端/小端和其他東西的問題。 Oracle可能會爲所有字節的值或反向位添加一個「服務」字節。這可能會更復雜一些,如果不對Oracle二進制文件進行逆向工程,就很難弄清楚。 –

相關問題