In der Anwendung, an der ich gerade arbeite, bin ich auf etwas Code gestoßen, der einen Datenbankaufruf lediglich zum Aufrufen der Funktion ORA_HASH
( documentation ) einer UUID-Zeichenfolge ausführt. Der Grund dafür ist, dass es den Wert benötigt, um einen Service-Aufruf an ein anderes System zu tätigen, das anscheinend ORA_HASH
für die Partitionierung verwendet.
Ich würde gerne wissen, welchen Algorithmus ORA_HASH
verwendet, damit ich ihn erneut implementieren kann, um einen ähnlichen Service-Aufruf für eine Anwendung durchzuführen, die keinen Zugriff auf eine echte Datenbank hat, ganz zu schweigen von Oracle. Ich konnte bisher nur herausfinden, was der Oracle API-Dokumentation entspricht.
Nur um ganz klar zu sein: Ich muss ORA_HASH
klonen, weil dies ein anderes System ist, das außerhalb meiner Kontrolle liegt, und ich muss in dieses System integrieren. Ja, es wäre schön, wenn Sie einen wirklich-Standardalgorithmus wie MD5 verwenden könnten, aber ich kann es nicht, es sei denn, ORA_HASH
steht unter der Decke.
Antworten oder Kommentare, die die Verwendung eines Hash-Algorithmus neben ORA_HASH
vorschlagen, sind nicht hilfreich. Diese Frage bezieht sich speziell auf ORA_HASH
, nicht generell auf Hashing oder Partitionierung.
ein anderes System, das scheinbar ORA_HASH verwendet
Wenn es "scheinbar zu verwenden" ist, ist es sinnvoll, ein wenig Reverse Engineering durchzuführen und zu überprüfen, was genau aufgerufen wird, und den Code der Funktion zu zerlegen.
Wenn Sie jedoch in die Interna von Oracle eintauchen möchten, kann das Folgende hilfreich sein.
Zunächst müssen Sie herausfinden, wie die interne C-Funktion ..__ benannt wird. Dazu können Sie in einer Sitzung lang laufenden Code ausführen. _ Ich habe dies ausgeführt
select avg(ora_hash(rownum)) id from
(select rownum from dual connect by rownum <= 1e4),
(select rownum from dual connect by rownum <= 1e4);
Es kann sich auch um PL/SQL-Code handeln. Sie müssen lediglich sicherstellen, dass Sie ora_hash ständig aufrufen.
Während es läuft
Wenn Sie unter Windows arbeiten, können Sie ostackprof von TANEL PODER ( Https://blog.tanelpoder.com/2008/10/31/advanced-Oracle-troblem-guide-part-9-process-stack) verwenden -profiling-from-sqlplus-using-ostackprof / )
Wenn Sie * nix verwenden, können Sie dtrace ( Http://www.Oracle.com/technetwork/articles/servers-storage-dev/dtrace-on-linux-1956556.html ) verwenden. , Flammendiagramm (Nutzungsszenario https://blog.dbi-services.com/Oracle-database-multilingual-engine-mle/ )
Ich habe unter Windows getestet und es sieht so aus, als ob ora_hash ist ...-> evaopn2 () -> evahash () -> ...
Lassen Sie uns nun auf evahash gehen. Wir hatten großes Glück, weil es auf der offiziellen Site https://oss.Oracle.com/projects/ocfs-tools/src/branches/new-dir-format/libocfs/Linux/inc/ocfshash) eine Header-Datei gibt. h mit Link zu evahash.
Und schließlich gibt es eine Seite mit aktuellem C-Code http://burtleburtle.net/bob/hash/evahash.html
Soweit so gut, erinnern wir uns daran, dass wir externe C-Funktionen in Oracle verwenden können, wenn wir sie in eine Bibliothek (DLL unter Windows) integrieren.
Zum Beispiel auf meinem Win x64, wenn ich die Funktionssignatur in ändere
extern "C" ub4 hash( ub1 *k, ub4 length, ub4 initval)
sie kann erfolgreich von Oracle aus ausgeführt werden . Wie Sie jedoch sehen, unterscheidet sich die Signatur in Oracle etwas von ora_hash. Diese Funktion akzeptiert value, seine Länge und initval (möglicherweise Seed), während die Signatur in Oracle ora_hash (expr, max_bucket, seed_value) ist.
Versuchen wir Oracle zu testen
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
Keine der Zahlen stimmt mit ..__ überein. Worin liegt das Problem? Ora_hash akzeptiert Parameter fast jedes Typs (beispielsweise select ora_hash(sys.odcinumberlist(1,2,3)) from dual
), während die C-Funktion Werte als Byte-Array akzeptiert. Dies bedeutet, dass einige Konvertierungen vor dem Funktionsaufruf .. stattfinden. Bevor Sie die erwähnte C-Hash-Funktion verwenden, müssen Sie herausfinden, wie der tatsächliche Wert transformiert wird, bevor Sie ihn übergeben.
Sie können mit dem Reverse-Engineering von Oracle-Binärdateien mit IDA PRO + -Hex-Strahlen fortfahren. Dies kann jedoch Tage dauern. Plattformspezifische Details ganz zu schweigen.
Wenn Sie ora_hash imitieren möchten, wäre die einfachste Option die Installation der Oracle Express Edition und die Verwendung von ora_hash.
Ich hoffe das war interessant. Viel Glück.
Update
ora_hash und dbms_utility.get_hash_value können einander zugeordnet werden (siehe 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
Wenn wir den Paketkörper von dbms_utility auspacken, sehen wir folgende Deklaration
function get_hash_value(name varchar2, base number, hash_size number)
return number is
begin
return(icd_hash(name, base, hash_size));
end;
und
function icd_hash(name varchar2,
base binary_integer,
hash_size binary_integer) return binary_integer;
pragma interface(c, icd_hash);
Lassen Sie uns google nach icd_hash
suchen, und wir können feststellen, dass es _psdhsh
( https://yurichev.com/blog/50/ ) zugeordnet ist. Jetzt ist es Zeit, Oracle.exe zu demontieren und Code für _psdhsh
daraus zu extrahieren. Vielleicht werde ich nächstes Jahr etwas Zeit damit verbringen.
Dies beantwortet nicht die OP-Frage nach dem eigentlichen Algorithmus hinter ora_hash. Dies ist nur ein Beispiel für die Verwendung von ora_hash in pl/sql (Antwort auf @JonHeller-Kommentar):
Die Funktion:
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.
Und es benutzen:
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-Ausgabe:
2662839991
Sie können auch mit RESULT_CACHE oder anderen Techniken herumspielen, um die Dinge noch schneller zu machen.
Es ist schon sehr schnell. Beispiel: Aufruf der Funktion 1 Million Mal für eine große Tabelle:
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.
Im Grunde also 100.000 Zeilen pro Sekunde, das schließt alle Kontextwechsel ein, um die Sie sich Sorgen machen müssen.
Wenn Sie ORA_HASH wegen der Leistung reproduzieren müssen, würde ich vorschlagen, dass Ihr Leistungsengpass möglicherweise an anderer Stelle liegt.