[20260824]数字字典对象hash的计算.txt

[20260824]数字字典对象hash的计算.txt

--//前几天探究row cache时,好奇对应的hash值如何计算,经过多次尝试,大致知道算法。

1.环境:
SYS@book> @ ver2
==============================
PORT_STRING                   : x86_64/Linux 2.4.xx
VERSION                       : 21.0.0.0.0
BANNER                        : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
BANNER_FULL                   : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
Version 21.3.0.0.0
BANNER_LEGACY                 : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
CON_ID                        : 0
PL/SQL procedure successfully completed.

2.提问:
--//在kimi上提问,给出计算调用kqrhsh函数,仔细查看反汇编真正计算的是kgghash函数。而且还问oracle可能使用的算法.
--//给出的结论,猜测3种算法,最大可能是Jenkins Lookup3,不熟悉也不懂,不过这个不重要。

--//摘要如下:
--//从 Oracle 内部函数的命名和历史实现来看,kgghash 很可能是Jenkins Lookup3.
--//典型情况是级联调用模式(用前次结果作 seed)是 lookup3 的典型用法.
--//猜测 A:Jenkins Lookup3 的 Oracle 变体
--//Oracle 内部大量使用 Bob Jenkins 的哈希算法。kgghash 的签名(data, len, seed)与 lookup3 完全一致.

--//如果我的理解没错的话,使用ora_hash函数应该也可以计算出结果.

$ cat dc_hash.bt
BEGIN
{
    printf("Tracing Oracle kgghash... Target PID: %d\n", $1);
    printf("Hit Ctrl-C to stop.\n\n");
}

uprobe:/u01/app/oracle/product/21.0.0/dbhome_1/bin/oracle:kqrhsh
/ $1 == 0 || pid == $1 /
{
        printf("\nkrqhsh Begin\n");
}

uretprobe:/u01/app/oracle/product/21.0.0/dbhome_1/bin/oracle:kqrhsh
/ $1 == 0 || pid == $1 /
{
        printf("krqhsh End\n");
}


uprobe:/u01/app/oracle/product/21.0.0/dbhome_1/bin/oracle:kgghash
/ $1 == 0 || pid == $1 /
{
    @kgg++;
    @is_kgg[pid] = 1;
//  printf("[%s] kgghash #%02d pid=%d hash_text=%s length=%d seed=%x hash_text_ascii = ", strftime("%H:%M:%S", nsecs), @kgg, pid,str(arg0,arg1+1),arg1,arg2);
//      print(buf(str(arg0,arg1+1),arg1));
//  printf("[%s] kgghash #%02d pid=%d hash_text=%s length=%d seed=%x \n", strftime("%H:%M:%S", nsecs), @kgg, pid,buf(str(arg0,arg1+1),arg1), arg1,arg2);
//  printf("xxxx = %s %s %s\n",str(arg0,arg1),str(arg0,arg1+1),buf(arg0,arg1));

//  printf("[%s] kgghash #%02d pid=%d hash_text=%s length=%d seed=%x \n", strftime("%H:%M:%S", nsecs), @kgg, pid,buf(str(arg0,arg1+1),arg1), arg1,arg2);
    printf("[%s] kgghash #%02d pid=%d hash_text=%s length=%d seed=%x \n", strftime("%H:%M:%S", nsecs), @kgg, pid,buf(arg0,arg1), arg1,arg2);
}

uretprobe:/u01/app/oracle/product/21.0.0/dbhome_1/bin/oracle:kgghash
/ $1 == 0 || pid == $1 /
{
    if (@is_kgg[pid] == 1) {
        printf("[%s] kgghash #%02d pid=%d return hash_value=%016lx %ld %016lx %d\n", strftime("%H:%M:%S", nsecs), @kgg,pid, retval, retval, retval%32768,retval%32768);
        delete(@is_kgg[pid]);
    }
}

END
{
    printf("\nTotal kgghash: %d\n", @kgg);
    delete(@kgg);

}


3.使用bpftrace跟踪:
SYS@book> @ spid
==============================
SID                           : 30
SERIAL#                       : 900
PROCESS                       : 3156
SERVER                        : DEDICATED
SPID                          : 3157
PID                           : 8
P_SERIAL#                     : 6
KILL_COMMAND                  : alter system kill session '30,900' immediate;
PL/SQL procedure successfully completed.

--//先在cdb下执行 select sid from v$mystat  where rownum=1;多次,切换界面执行# bpftrace ./dc_hash.bt 3157。
SYS@book> select sid from v$mystat  where rownum=1;

       SID
----------
        30

 # bpftrace dc_hash.bt 3157
Attached 6 probes
Tracing Oracle kgghash... Target PID: 3157
Hit Ctrl-C to stop.

[15:45:21] kgghash #01 pid=3157 hash_text=CDB$ROOT length=8 seed=0
[15:45:21] kgghash #01 pid=3157 return hash_value=000000001eee6512 518939922 0000000000006512 25874

krqhsh Begin
[15:45:21] kgghash #02 pid=3157 hash_text=\xc3\xc0\xff\xff length=4 seed=0
[15:45:21] kgghash #02 pid=3157 return hash_value=00000000f28adaf8 4069186296 0000000000005af8 23288
[15:45:21] kgghash #03 pid=3157 hash_text=CDB$ROOT length=8 seed=f28adaf8
[15:45:21] kgghash #03 pid=3157 return hash_value=0000000046ad9a7a 1185782394 0000000000001a7a 6778
krqhsh End

krqhsh Begin
[15:45:21] kgghash #04 pid=3157 hash_text=\xc3\xc0\xff\xff length=4 seed=0
[15:45:21] kgghash #04 pid=3157 return hash_value=00000000f28adaf8 4069186296 0000000000005af8 23288
[15:45:21] kgghash #05 pid=3157 hash_text=CDB$ROOT length=8 seed=f28adaf8
[15:45:21] kgghash #05 pid=3157 return hash_value=0000000046ad9a7a 1185782394 0000000000001a7a 6778
krqhsh End

krqhsh Begin
[15:45:21] kgghash #06 pid=3157 hash_text=\xfa\xc3\xff\xff length=4 seed=0
[15:45:21] kgghash #06 pid=3157 return hash_value=00000000368bf4a4 915141796 00000000000074a4 29860
[15:45:21] kgghash #07 pid=3157 hash_text=CDB$ROOT length=8 seed=368bf4a4
[15:45:21] kgghash #07 pid=3157 return hash_value=0000000040d79a79 1087871609 0000000000001a79 6777
krqhsh End

krqhsh Begin
[15:45:21] kgghash #08 pid=3157 hash_text=\xfa\xc3\xff\xff length=4 seed=0
[15:45:21] kgghash #08 pid=3157 return hash_value=00000000368bf4a4 915141796 00000000000074a4 29860
[15:45:21] kgghash #09 pid=3157 hash_text=CDB$ROOT length=8 seed=368bf4a4
[15:45:21] kgghash #09 pid=3157 return hash_value=0000000040d79a79 1087871609 0000000000001a79 6777
krqhsh End
[15:45:21] kgghash #10 pid=3157 hash_text=\x0b\x00\x01\x00\x00\x00\x00\x00 length=8 seed=0
[15:45:21] kgghash #10 pid=3157 return hash_value=00000000ceb5d99e 3468024222 000000000000599e 22942

--//简单解析:
--//hash_text=\xc3\xc0\xff\xff, intel cpu 大小头问题,大小头对调对应整形
--//0xffffc0c3 = 4294951107
--//0xffffc3fa = 4294951930

SYS@book> @ xtab  X$KSUSGIF|^V$MYSTAT
   INST_ID NAME                            OBJECT_ID TYPE                            TABLE_NUM     CON_ID
---------- ------------------------------ ---------- ------------------------------ ---------- ----------
         1 X$KSUSGIF                      4294951930 TABLE                                  52          0
         1 V$MYSTAT                       4294951107 VIEW                                65537          0

4.先简单验证:

[15:45:21] kgghash #01 pid=3157 hash_text=CDB$ROOT length=8 seed=0
[15:45:21] kgghash #01 pid=3157 return hash_value=000000001eee6512 518939922 0000000000006512 25874

--//0xffffffff = 4294967295

$ cat ora_hash1.txt
select
 ora_hash('&&1',4294967295,&2) n20
,to_char(ora_hash('&1',4294967295,&2),'FM0xxxxxxxxxxxxxxx')  c20
from dual;

--//缺省ora_hash函数最大max_bucket=2^3201=4294967295,seed=0,采用如下计算结果一样。
SYS@book> select to_char(ora_hash('CDB$ROOT'),'FM0xxxxxxxxxxxxxxx') c20 from dual;
C20
--------------------
000000001eee6512

SYS@book> @ ora_hash1.txt  CDB$ROOT 0
                  N20 C20
--------------------- --------------------
            518939922 000000001eee6512
--//说明推断可能正确,如果这步不正确的话,估计我当时就放弃猜测。

--//再验证如下:
krqhsh Begin
[15:45:21] kgghash #02 pid=3157 hash_text=\xc3\xc0\xff\xff length=4 seed=0
[15:45:21] kgghash #02 pid=3157 return hash_value=00000000f28adaf8 4069186296 0000000000005af8 23288
[15:45:21] kgghash #03 pid=3157 hash_text=CDB$ROOT length=8 seed=f28adaf8
[15:45:21] kgghash #03 pid=3157 return hash_value=0000000046ad9a7a 1185782394 0000000000001a7a 6778
krqhsh End

--//这里的hash_text 对应的ascii码\xc3\xc0\xff\xff,注:bpftrace的buf函数如果字符可以显示,直接输出对应字符。
--//为了测试方便另外写一个脚本:

$ cat ora_hash2.txt
select
 ora_hash(UTL_RAW.cast_to_varchar2(hextoraw('&1')),4294967295,&2) n20
,to_char(ora_hash(UTL_RAW.cast_to_varchar2(hextoraw('&1')),4294967295,&2),'FM0xxxxxxxxxxxxxxx')  c20
from dual;

[15:45:21] kgghash #02 pid=3157 hash_text=\xc3\xc0\xff\xff length=4 seed=0
[15:45:21] kgghash #02 pid=3157 return hash_value=00000000f28adaf8 4069186296 0000000000005af8 23288

SYS@book> @ ora_hash2.txt c3c0ffff 0
                  N20 C20
--------------------- --------------------
           4069186296 00000000f28adaf8
--//hash_value=00000000f28adaf8完全对上。

--//然后级联调用模式,可以理解为迭代(不知道是否正确),seed=4069186296继续运算。
[15:45:21] kgghash #03 pid=3157 hash_text=CDB$ROOT length=8 seed=f28adaf8
[15:45:21] kgghash #03 pid=3157 return hash_value=0000000046ad9a7a 1185782394 0000000000001a7a 6778

SYS@book> @ ora_hash1.txt  CDB$ROOT 4069186296
                  N20 C20
--------------------- --------------------
           1185782394 0000000046ad9a7a
--//hash_value=0000000046ad9a7a完全对上,证明我的猜测正确。

SYS@book> select mod(1185782394,power(2,15)) from dual ;
MOD(1185782394,POWER(2,15))
---------------------------
                       6778
--//6778= 0x1a7a

--//再来验证如下情况:
krqhsh Begin
[15:45:21] kgghash #08 pid=3157 hash_text=\xfa\xc3\xff\xff length=4 seed=0
[15:45:21] kgghash #08 pid=3157 return hash_value=00000000368bf4a4 915141796 00000000000074a4 29860
[15:45:21] kgghash #09 pid=3157 hash_text=CDB$ROOT length=8 seed=368bf4a4
[15:45:21] kgghash #09 pid=3157 return hash_value=0000000040d79a79 1087871609 0000000000001a79 6777
krqhsh End

SYS@book> @ ora_hash2.txt  fac3ffff 0
                  N20 C20
--------------------- --------------------
            915141796 00000000368bf4a4

SYS@book> @ ora_hash1.txt  CDB$ROOT 915141796
                  N20 C20
--------------------- --------------------
           1087871609 0000000040d79a79

SYS@book> select mod(1087871609,power(2,15)) from dual ;
MOD(1087871609,POWER(2,15))
---------------------------
                       6777
--//6777 = 0x1a79,两者不知道是否巧合正好hash相差1.

--//继续验证:
[15:45:21] kgghash #10 pid=3157 hash_text=\x0b\x00\x01\x00\x00\x00\x00\x00 length=8 seed=0
[15:45:21] kgghash #10 pid=3157 return hash_value=00000000ceb5d99e 3468024222 000000000000599e 22942

SYS@book> @ ora_hash2.txt 0b00010000000000 0
                  N20 C20
--------------------- --------------------
           3468024222 00000000ceb5d99e
--//完全对上。

4.小结:
--//数据字典对象的hash计算基本通过类似ora_hash函数,参与运算的数据包括对象属性,比如前面测试的情况object_id,cdb_name之类
--//属性,然后不断级联迭代得到hash结果。不同类型对象参与计算属性可能不同,没有必要追求这些细节。
posted @ 2026-08-27 21:21  lfree  阅读(2)  评论(0)    收藏  举报