[20250202]21c library cache mutex的深入探究5.txt
[20250202]21c library cache mutex的深入探究5.txt
--//前面的学习已经了解21c library cache mutex chunk分布的特点,再次总结如下:
--//1个chunk 有256个mutex地址,占用12304,每个前面有16字节开头,许多chunk在内存分布上紧挨的。共计512个chunk。
--//在1个chunk内muetx每个偏移48字节.
--//我的测试被分成3个大片:注:我在另外一台生产系统看到分成2个大片。
--//通过fchaz脚本查询对应的KSMCHPAR,没有信息输出。
--//问题来了,如果第一次执行一条sql语句,知道bucket桶号,oracle如何定位到相应mutex地址。
--//在11g下开始学习以为整个在一个chunk里面,这样知道基地址加上偏移量就可以知道buckect桶号就可以定位mutex地址。
--//实际上的情况在21c分布在存在于512个chunk中,前面的测试许多chunk在内存分布上紧挨的,分成3个大片。显然不能通过上面简单
--//计算定位mutex地址。
--//在11g下通过查询x$ksmmem,获得相应值,而21c下遇到问题,展开下面的分析。
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.简单介绍:
SYS@book> select count(*),KSMCHPAR from x$ksmsp where KSMCHCOM='KGLSG' and KSMCHSIZ=12304 group by KSMCHPAR;
COUNT(*) KSMCHPAR
---------- ----------------
31 000000006CC04000
339 000000006C804000
142 000000006C434000
--//Sum = 512。
--//前面的测试在一个chunk内每个mutex地址相邻相差48字节,48*256+16 = 12304,也就是1个chunk保存256个mutex地址。
SYS@book> @ fchaz 000000006CC04000
no rows selected
SYS@book> @ fchaz 000000006C804000
no rows selected
SYS@book> @ fchaz 000000006C434000
no rows selected
--//堆描述符并没有占用空间,我个人认为启动时存在的,建立library cache mutex的chunk后删除了。
SYS@book> select * from (select * from x$ksmmem order by addr desc) where rownum=1;
ADDR INDX INST_ID CON_ID KSMMMVAL
---------------- ---------- ---------- ---------- ----------------
000000006093DFA0 1211380 1 1 00
--//使用x$ksmmem查询的最大地址是000000006093DFA0.而实际上sga占用内存分布如下:
$ pmap -x $(pgrep pmon) | egrep -i "sysv|^Address"
Address Kbytes RSS Dirty Mode Mapping
0000000060000000 10240 0 0 rw-s- SYSV00000000 (deleted)
~~~~~~~~~~~~~~~~
0000000060c00000 770048 0 0 rw-s- SYSV00000000 (deleted)
000000008fc00000 8192 0 0 rw-s- SYSV00000000 (deleted)
--//使用x$ksmmem仅仅查询到下划线的内存区域,不能再使用它查询整个sga。
--//不过在11g的测试中已经知道其内存地址就在bucket =0 的mutex地址前面的。
--//注:11g 下在同1个chunk,21c情况不是。
3.分析:
--//转储library_cache:
SCOTT@book01p> select /*+ 9 */ count(*) from dept where deptno = 93834;
COUNT(*)
----------
0
SCOTT@book01p> @ hash
HASH_VALUE SQL_ID CHILD_NUMBER KGL_BUCKET PLAN_HASH_VALUE HASH_HEX SQL_EXEC_START SQL_EXEC_ID
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
3403546624 as3g00v5dw000 0 0 2236899148 cade0000 2025-02-02 15:19:04 16777221
SYS@book> oradebug setmypid
Statement processed.
SYS@book> oradebug dump library_cache 4
Statement processed.
$ grep "^Bucket:" /u01/app/oracle/diag/rdbms/book/book/trace/book_ora_10770.trc | head -4
Bucket: #=0 Mutex=0x6cfa1400(1125281431552, 9, 0, 6)
Bucket: #=5 Mutex=0x6cfa14f0(1125281431552, 4, 0, 6)
Bucket: #=17 Mutex=0x6cfa1730(1125281431552, 8, 0, 6)
Bucket: #=39 Mutex=0x6cfa1b50(1125281431552, 4, 0, 6)
--//Bucket: #=0 Mutex=0x6cfa1400
--//0x6cfa1400-0x10 = 0x6cfa13f0
SYS@book> @ fchaz 0x6cfa1400
LOC KSMCHPTR KSMCHIDX KSMCHDUR KSMCHCOM KSMCHSIZ KSMCHCLS KSMCHTYP KSMCHPAR KSMCHPTR_BEGIN KSMCHPTR_END+1
--- ---------------- ---------- ---------- ---------------- ---------- -------- ---------- ---------------- ---------------- -----------------
SGA 000000006CFA13E0 1 1 KGLSG 12304 perm 0 000000006CC04000 000000006CFA13E0 000000006CFA43F0
--//0x000000006CFA13E0-0x1 = 0x6cfa13df
SYS@book> @ fchaz 000000006CFA13df
LOC KSMCHPTR KSMCHIDX KSMCHDUR KSMCHCOM KSMCHSIZ KSMCHCLS KSMCHTYP KSMCHPAR KSMCHPTR_BEGIN KSMCHPTR_END+1
--- ---------------- ---------- ---------- ---------------- ---------- -------- ---------- ---------------- ---------------- -----------------
SGA 000000006CFA03C0 1 1 KGLSG 4128 perm 0 000000006CC04000 000000006CFA03C0 000000006CFA13E0
--//在Bucket: #=0前面的chunk大小KSMCHSIZ=4128,KSMCHCOM=KGLSG(类型与后面的chunk一样)。
--//如果每个地址占用8个字节,共计512个,4128-512*8 = 32。基本猜测正确,验证看看。
SYS@book> @ opeek 000000006CFA03C0 64 1
New tracefile_identifier = /u01/app/oracle/diag/rdbms/book/book/trace/book_ora_3412_0005.trc
[06CFA03C0, 06CFA0400) = 00001021 00B38F00 17ADF8D0 00000000 6CFA03E0 00000000 000016E8 00020000 6CFA13F0 00000000 6CFA4400 00000000 6CFA7410 00000000 ...
~~~~~~~~ ~~~~~~~~~ ~~~~~~~~
--//0x00001021 = 4129 = 4128+1 ,看来chunk的开头前4字节保存的是chunk大小+1.
--//注意看下划线值(前面占用32字节),正好等于0x6cfa1400-0x10 = 0x6cfa13f0。
--//0x6CFA4400-0x6CFA13F0 = 12304
--//0x6CFA7410-0x6CFA4400 = 12304
--//前后相减正好等于12304.
--//实际上很简单在一个chunk里面记录了一张表或者讲一个数组,记录数量为2^_kgl_bucket_count,每个占8字节(我的OS 64位系统),相
--//当于N*256的mutex地址(N=0到511),假设知道基地址A后,如果知道bucket值.使用bucket/256 取整就可以定位 该地址B 保存在
--//A + trunc(bucket/256)*8的位置,再通过bucket%256 * 40 + B , 该位置就保存了该bucket的library cache mutex的地址。
4.验证分成3个大片:
SYS@book> @ opeek 000000006CFA03C0 4128 1
New tracefile_identifier = /u01/app/oracle/diag/rdbms/book/book/trace/book_ora_3412_0007.trc
[06CFA03C0, 06CFA13E0) = 00001021 00B38F00 17ADF8D0 00000000 6CFA03E0 00000000 0000188E 00020000 6CFA13F0 00000000 6CFA4400 00000000 6CFA7410 00000000 ...
$ sed -n "/^06CFA03E0/,/^06CFA13D0/p" /u01/app/oracle/diag/rdbms/book/book/trace/book_ora_3412_0007.trc | awk '{print $2"\n"$4}' > d2.txt
$ head -4 d2.txt
6CFA13F0
6CFA4400
6CFA7410
6CFAA420
$ wc d2.txt
512 512 4608 d2.txt
$ awk 'NR==1{a=$1} NR>1{ print $1,a,strtonum("0x"$1)-strtonum("0x"a);a=$1}' d2.txt | head -10
6CFA4400 6CFA13F0 12304
6CFA7410 6CFA4400 12304
6CFAA420 6CFA7410 12304
6CFAD430 6CFAA420 12304
6CFB0440 6CFAD430 12304
6CFB3450 6CFB0440 12304
6CFB6460 6CFB3450 12304
6CFB9470 6CFB6460 12304
6CFBC480 6CFB9470 12304
6CFBF490 6CFBC480 12304
$ awk 'NR==1{a=$1} NR>1{ print $1,a,strtonum("0x"$1)-strtonum("0x"a);a=$1}' d2.txt | awk '{print $3}'| uniq -c
30 12304
1 -8353184
338 12304
1 -8156448
141 12304
--//与前面的select count(*),KSMCHPAR from x$ksmsp where KSMCHCOM='KGLSG' and KSMCHSIZ=12304 group by KSMCHPAR;输出可以对上。
5.顺便找一条实际的sql语句测试看看。
SCOTT@book01p> select * from dept where deptno = 40 ;
DEPTNO DNAME LOC
---------- ------------------------------ -------------
40 OPERATIONS BOSTON
SCOTT@book01p> @ hash
HASH_VALUE SQL_ID CHILD_NUMBER KGL_BUCKET PLAN_HASH_VALUE HASH_HEX SQL_EXEC_START SQL_EXEC_ID
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
61084640 2k056r41u84z0 0 5088 2852011669 3a413e0 2025-02-02 16:22:05 16777216
--//KGL_BUCKET=5088,计算就是使用HASH_VALUE % (2^_kgl_bucket_count * 256),缺省_kgl_bucket_count=9.61084640%2^17 = 5088
--//5088/256 = 19.875,取整就是19,取模 5088%256 = 224 ,224 = 0xe0
$ sed -n "20,20p" d2.txt
6CFDA520
--//注意从0开始计数,相当于取20行的记录。
--///6CFDA520+0x30*0xe0 = 0x6cfdcf20
SYS@book> @ opeek 0x6cfdcf20 48 0
[06CFDCF20, 06CFDCF50) = 671C92D8 00000000 671C92D8 00000000 00000000 00000000 0000000B 00000000 000013E0 00000000 64CEEB50 00000000
SYS@book> @ sharepool/shp4z 2k056r41u84z0 -1 ''
HANDLE_TYPE KGLHDADR KGLHDPAR C40 KGLHDLMD KGLHDPMD KGLHDIVC KGLOBHD0 KGLOBHD6 KGLOBHS0 KGLOBHS6 KGLOBT16 N0_6_16 N20 KGLNAHSH KGLOBT03 KGLOBT09
---------------------- ---------------- ---------------- ---------------------------------------- ---------- ---------- ---------- ---------------- ---------------- ---------- ---------- ---------- --------- ---------- ---------- ------------- ----------
parent handle address 00000000671C92D8 00000000671C92D8 select * from dept where deptno = 40 1 0 0 00000000679A7B48 00 4064 0 0 4064 4064 61084640 2k056r41u84z0 65535
--//parent handle address=00000000671C92D8,与opeek的输出前面16字节 能够对上。
--//0x000013E0 = 5088 ,正好等于bucket桶号。
6.小结:
--//实际上很简单在一个chunk里面记录了一张表或者讲一个数组,记录数量为2^_kgl_bucket_count,每个占8字节(我的OS 64位系统),相
--//当于N*256的mutex地址(N=0到511),假设知道基地址A后,如果知道bucket值.使用bucket/256 取整就可以定位 该地址B 保存在
--//A + trunc(bucket/256)*8的位置,再通过bucket%256 * 40 + B , 该位置就保存了该bucket的library cache mutex的地址。
--//前面的学习已经了解21c library cache mutex chunk分布的特点,再次总结如下:
--//1个chunk 有256个mutex地址,占用12304,每个前面有16字节开头,许多chunk在内存分布上紧挨的。共计512个chunk。
--//在1个chunk内muetx每个偏移48字节.
--//我的测试被分成3个大片:注:我在另外一台生产系统看到分成2个大片。
--//通过fchaz脚本查询对应的KSMCHPAR,没有信息输出。
--//问题来了,如果第一次执行一条sql语句,知道bucket桶号,oracle如何定位到相应mutex地址。
--//在11g下开始学习以为整个在一个chunk里面,这样知道基地址加上偏移量就可以知道buckect桶号就可以定位mutex地址。
--//实际上的情况在21c分布在存在于512个chunk中,前面的测试许多chunk在内存分布上紧挨的,分成3个大片。显然不能通过上面简单
--//计算定位mutex地址。
--//在11g下通过查询x$ksmmem,获得相应值,而21c下遇到问题,展开下面的分析。
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.简单介绍:
SYS@book> select count(*),KSMCHPAR from x$ksmsp where KSMCHCOM='KGLSG' and KSMCHSIZ=12304 group by KSMCHPAR;
COUNT(*) KSMCHPAR
---------- ----------------
31 000000006CC04000
339 000000006C804000
142 000000006C434000
--//Sum = 512。
--//前面的测试在一个chunk内每个mutex地址相邻相差48字节,48*256+16 = 12304,也就是1个chunk保存256个mutex地址。
SYS@book> @ fchaz 000000006CC04000
no rows selected
SYS@book> @ fchaz 000000006C804000
no rows selected
SYS@book> @ fchaz 000000006C434000
no rows selected
--//堆描述符并没有占用空间,我个人认为启动时存在的,建立library cache mutex的chunk后删除了。
SYS@book> select * from (select * from x$ksmmem order by addr desc) where rownum=1;
ADDR INDX INST_ID CON_ID KSMMMVAL
---------------- ---------- ---------- ---------- ----------------
000000006093DFA0 1211380 1 1 00
--//使用x$ksmmem查询的最大地址是000000006093DFA0.而实际上sga占用内存分布如下:
$ pmap -x $(pgrep pmon) | egrep -i "sysv|^Address"
Address Kbytes RSS Dirty Mode Mapping
0000000060000000 10240 0 0 rw-s- SYSV00000000 (deleted)
~~~~~~~~~~~~~~~~
0000000060c00000 770048 0 0 rw-s- SYSV00000000 (deleted)
000000008fc00000 8192 0 0 rw-s- SYSV00000000 (deleted)
--//使用x$ksmmem仅仅查询到下划线的内存区域,不能再使用它查询整个sga。
--//不过在11g的测试中已经知道其内存地址就在bucket =0 的mutex地址前面的。
--//注:11g 下在同1个chunk,21c情况不是。
3.分析:
--//转储library_cache:
SCOTT@book01p> select /*+ 9 */ count(*) from dept where deptno = 93834;
COUNT(*)
----------
0
SCOTT@book01p> @ hash
HASH_VALUE SQL_ID CHILD_NUMBER KGL_BUCKET PLAN_HASH_VALUE HASH_HEX SQL_EXEC_START SQL_EXEC_ID
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
3403546624 as3g00v5dw000 0 0 2236899148 cade0000 2025-02-02 15:19:04 16777221
SYS@book> oradebug setmypid
Statement processed.
SYS@book> oradebug dump library_cache 4
Statement processed.
$ grep "^Bucket:" /u01/app/oracle/diag/rdbms/book/book/trace/book_ora_10770.trc | head -4
Bucket: #=0 Mutex=0x6cfa1400(1125281431552, 9, 0, 6)
Bucket: #=5 Mutex=0x6cfa14f0(1125281431552, 4, 0, 6)
Bucket: #=17 Mutex=0x6cfa1730(1125281431552, 8, 0, 6)
Bucket: #=39 Mutex=0x6cfa1b50(1125281431552, 4, 0, 6)
--//Bucket: #=0 Mutex=0x6cfa1400
--//0x6cfa1400-0x10 = 0x6cfa13f0
SYS@book> @ fchaz 0x6cfa1400
LOC KSMCHPTR KSMCHIDX KSMCHDUR KSMCHCOM KSMCHSIZ KSMCHCLS KSMCHTYP KSMCHPAR KSMCHPTR_BEGIN KSMCHPTR_END+1
--- ---------------- ---------- ---------- ---------------- ---------- -------- ---------- ---------------- ---------------- -----------------
SGA 000000006CFA13E0 1 1 KGLSG 12304 perm 0 000000006CC04000 000000006CFA13E0 000000006CFA43F0
--//0x000000006CFA13E0-0x1 = 0x6cfa13df
SYS@book> @ fchaz 000000006CFA13df
LOC KSMCHPTR KSMCHIDX KSMCHDUR KSMCHCOM KSMCHSIZ KSMCHCLS KSMCHTYP KSMCHPAR KSMCHPTR_BEGIN KSMCHPTR_END+1
--- ---------------- ---------- ---------- ---------------- ---------- -------- ---------- ---------------- ---------------- -----------------
SGA 000000006CFA03C0 1 1 KGLSG 4128 perm 0 000000006CC04000 000000006CFA03C0 000000006CFA13E0
--//在Bucket: #=0前面的chunk大小KSMCHSIZ=4128,KSMCHCOM=KGLSG(类型与后面的chunk一样)。
--//如果每个地址占用8个字节,共计512个,4128-512*8 = 32。基本猜测正确,验证看看。
SYS@book> @ opeek 000000006CFA03C0 64 1
New tracefile_identifier = /u01/app/oracle/diag/rdbms/book/book/trace/book_ora_3412_0005.trc
[06CFA03C0, 06CFA0400) = 00001021 00B38F00 17ADF8D0 00000000 6CFA03E0 00000000 000016E8 00020000 6CFA13F0 00000000 6CFA4400 00000000 6CFA7410 00000000 ...
~~~~~~~~ ~~~~~~~~~ ~~~~~~~~
--//0x00001021 = 4129 = 4128+1 ,看来chunk的开头前4字节保存的是chunk大小+1.
--//注意看下划线值(前面占用32字节),正好等于0x6cfa1400-0x10 = 0x6cfa13f0。
--//0x6CFA4400-0x6CFA13F0 = 12304
--//0x6CFA7410-0x6CFA4400 = 12304
--//前后相减正好等于12304.
--//实际上很简单在一个chunk里面记录了一张表或者讲一个数组,记录数量为2^_kgl_bucket_count,每个占8字节(我的OS 64位系统),相
--//当于N*256的mutex地址(N=0到511),假设知道基地址A后,如果知道bucket值.使用bucket/256 取整就可以定位 该地址B 保存在
--//A + trunc(bucket/256)*8的位置,再通过bucket%256 * 40 + B , 该位置就保存了该bucket的library cache mutex的地址。
4.验证分成3个大片:
SYS@book> @ opeek 000000006CFA03C0 4128 1
New tracefile_identifier = /u01/app/oracle/diag/rdbms/book/book/trace/book_ora_3412_0007.trc
[06CFA03C0, 06CFA13E0) = 00001021 00B38F00 17ADF8D0 00000000 6CFA03E0 00000000 0000188E 00020000 6CFA13F0 00000000 6CFA4400 00000000 6CFA7410 00000000 ...
$ sed -n "/^06CFA03E0/,/^06CFA13D0/p" /u01/app/oracle/diag/rdbms/book/book/trace/book_ora_3412_0007.trc | awk '{print $2"\n"$4}' > d2.txt
$ head -4 d2.txt
6CFA13F0
6CFA4400
6CFA7410
6CFAA420
$ wc d2.txt
512 512 4608 d2.txt
$ awk 'NR==1{a=$1} NR>1{ print $1,a,strtonum("0x"$1)-strtonum("0x"a);a=$1}' d2.txt | head -10
6CFA4400 6CFA13F0 12304
6CFA7410 6CFA4400 12304
6CFAA420 6CFA7410 12304
6CFAD430 6CFAA420 12304
6CFB0440 6CFAD430 12304
6CFB3450 6CFB0440 12304
6CFB6460 6CFB3450 12304
6CFB9470 6CFB6460 12304
6CFBC480 6CFB9470 12304
6CFBF490 6CFBC480 12304
$ awk 'NR==1{a=$1} NR>1{ print $1,a,strtonum("0x"$1)-strtonum("0x"a);a=$1}' d2.txt | awk '{print $3}'| uniq -c
30 12304
1 -8353184
338 12304
1 -8156448
141 12304
--//与前面的select count(*),KSMCHPAR from x$ksmsp where KSMCHCOM='KGLSG' and KSMCHSIZ=12304 group by KSMCHPAR;输出可以对上。
5.顺便找一条实际的sql语句测试看看。
SCOTT@book01p> select * from dept where deptno = 40 ;
DEPTNO DNAME LOC
---------- ------------------------------ -------------
40 OPERATIONS BOSTON
SCOTT@book01p> @ hash
HASH_VALUE SQL_ID CHILD_NUMBER KGL_BUCKET PLAN_HASH_VALUE HASH_HEX SQL_EXEC_START SQL_EXEC_ID
---------- ------------- ------------ ---------- --------------- ---------- ------------------- -----------
61084640 2k056r41u84z0 0 5088 2852011669 3a413e0 2025-02-02 16:22:05 16777216
--//KGL_BUCKET=5088,计算就是使用HASH_VALUE % (2^_kgl_bucket_count * 256),缺省_kgl_bucket_count=9.61084640%2^17 = 5088
--//5088/256 = 19.875,取整就是19,取模 5088%256 = 224 ,224 = 0xe0
$ sed -n "20,20p" d2.txt
6CFDA520
--//注意从0开始计数,相当于取20行的记录。
--///6CFDA520+0x30*0xe0 = 0x6cfdcf20
SYS@book> @ opeek 0x6cfdcf20 48 0
[06CFDCF20, 06CFDCF50) = 671C92D8 00000000 671C92D8 00000000 00000000 00000000 0000000B 00000000 000013E0 00000000 64CEEB50 00000000
SYS@book> @ sharepool/shp4z 2k056r41u84z0 -1 ''
HANDLE_TYPE KGLHDADR KGLHDPAR C40 KGLHDLMD KGLHDPMD KGLHDIVC KGLOBHD0 KGLOBHD6 KGLOBHS0 KGLOBHS6 KGLOBT16 N0_6_16 N20 KGLNAHSH KGLOBT03 KGLOBT09
---------------------- ---------------- ---------------- ---------------------------------------- ---------- ---------- ---------- ---------------- ---------------- ---------- ---------- ---------- --------- ---------- ---------- ------------- ----------
parent handle address 00000000671C92D8 00000000671C92D8 select * from dept where deptno = 40 1 0 0 00000000679A7B48 00 4064 0 0 4064 4064 61084640 2k056r41u84z0 65535
--//parent handle address=00000000671C92D8,与opeek的输出前面16字节 能够对上。
--//0x000013E0 = 5088 ,正好等于bucket桶号。
6.小结:
--//实际上很简单在一个chunk里面记录了一张表或者讲一个数组,记录数量为2^_kgl_bucket_count,每个占8字节(我的OS 64位系统),相
--//当于N*256的mutex地址(N=0到511),假设知道基地址A后,如果知道bucket值.使用bucket/256 取整就可以定位 该地址B 保存在
--//A + trunc(bucket/256)*8的位置,再通过bucket%256 * 40 + B , 该位置就保存了该bucket的library cache mutex的地址。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
2024-02-19 [20240219]建立完善sql_idx.sh脚本.txt
2019-02-19 [20190219]那个更快(11g).txt