多谢Yong Huang 得回复
后来我又看了 eygle 的关于shared pool的深入探讨 相关的文章
后来把library cache 内存里面的东西dump 出来看了一下 ,稍微有点头绪
在share_pool 里面的空间管理也是通过 hash buckets (hash 桶)来实现的 ,其实以前我们在学操作系统的时候 ,一般内存管理都是通过
一个链表来管理的 ,比如说空闲内存的链表 ,整个链表按空闲内存的大小进行排序 ,当我们要找一个空闲内存的时候 ,然后给链表上锁 ,搜索整个
链表,直到找到一个适合自己的 ,然后将该节点摘下 ,减去需要的内存后,再把该节点重新插入到 整个 链表中 ,其实就是再排序了 ,这样的话可能会造成
碎片的增多,就就造成链表长度的增长 ,使查询效率降低 ,所以操作系统中过段时间需要合并内存碎片,一个是可以申请更大的内存区域 ,还有就是减少查找
次数。
但是oracle 里面使用了更好的方法来管理share_pool ,因为sql 的内存申请一般比较小,通常都是只有几k ,如果纯粹使用链表来管理 ,那势必会极大的降低并发性能
,因此可以使用 hash buckets 来管理 ,每一个桶里面放一定大小的空闲空间,当要申请空间的时候,先做hash 映射 ,找到该桶,然后再搜索链表,C描述如下:
typedef struct
(
int size ; // 该桶内放的空闲内存的大小(其实是范围)
struct free_mem * head ;
..... //其他字段
)hash_buckets;
char share_pool_latch ; //保护内存分配的latch
当数据库刚起来的时候 ,先初始化结构数组
struct hash_buckets[N] ; //可能每个数据库版本初始化的结构数组都不同。如9i的就比8i的多
初始化以后,刚开始肯定空闲空间都比较大 ,但数据库运行一段时间后 ,就会出现碎片,比如说 hash_buckets[1] 里面放的是 14k~20k 的空闲块 ,
hash_buckets[2] 里面放的是 21k~27k 的空闲块,现在需要申请一个 26k的空闲块了 ,那可以先对空闲块大小做映射 ,然后找到hash_buckets数组,然后再找到
相关链表的头部;
比如说分配内存可能会有如下代码:
int * mallc(int mallc_size)
{
..........
struct free_mem *head;
int hashvalue=hash_map(mallc_size); //根据需要分配的大小做hash映射 成 数组下标
while(1)
{
if(count>N) // 假如尝试的次数大于某个数值时
return null;
testandset(share_pool_latch) ; //内存分配的临界区
if(error) //这边testandset机器指令的执行结果应该存在CPU某个寄存器中
sleep(3) //比如这边休息3秒 ,3秒后再由操作系统放入运行队列中 .也就是说明 willing-to-wait 什么的
count++; //失败的次数
}
head=hash_buckets[hashvalue].head;
for( ;head

{
head=head->next;
.......
} //直到找到合适自己的 ,如果减去自己需要申请的 ,还需要把剩余空闲节点放到相应的hash_bucket 中去
。。。。。。。
testandclear(share_pool_latch);
}
如果说每个运行的oracle 进程都会有上面一个代码影像 ,那么在windows 中还比较好考虑,因为在windows 里面都是以线程跑的 ,所以 char share_pool_latch ; 这个变量是所以线程共享的 ,但如果在unix 中呢? 每个连接都是以进程方式跑的,如果每个进程都有一个share_pool_latch 变量的拷贝 ,那么testandclear() 这个指令就不起作用的 ? 在unix 中难道 share_pool_latch 这个变量是由操作系统来管理的 ? 那么对每个变量的操作就需要系统调用 ,势必会浪费不少时间(进程的上下文切换以及。。。) ,还是将mallc 函数封装在 so ,即动态连接库中 ? 这样就可以有一份变量拷贝? // 这边是一个疑问? who knows?
上面是空间的管理 ,然后就是涉及到library cache 中的 object 的管理 ,似乎oracle 中很多都是通过hash buckets 来管理的 ,而不是有纯粹的链表形式,这样能
增加并发性吧
基本数据结构如下:
typedef struct
(
......;
strcut LIBRARY_OBJECT_HANDLE * head ; //这边的handle 翻译成句柄 ,一直不明白为什么解释成这样,打开一个文件的时候有个文件句柄,就理解成指针吧
........
)lib_cache_hash_buckets;
typedef struct
(
char * name ;
char* hash;
char * namespace;
//char lock=0 pin=0 latch#=1 ???
..................
struct children_LIBRARY_OBJECT_HANDLE [N] ; //存放副本
struct data[N] ; //
......... //这个struct 中应该还有 union 结构,因为不仅存放sql ,还有存储过程,触发器之类的东西,用union 可以节省内存
) LIBRARY_OBJECT_HANDLE ;
children_LIBRARY_OBJECT_HANDLE 存放的就是这个sql 的副本 ,也有很多人称呼
LIBRARY_OBJECT_HANDLE 为 sql header , children_LIBRARY_OBJECT_HANDLE 为sql body
一个header 有多个 body ,因为sql 可能相同,但查询的表可能属于不同的用户,所以会有多个版本 ,似乎v$sqlarea 的version_count 来记录这个的
在每个body 中存放的是执行计划,还有什么?? //这边不清楚???
还有在 sql header 中存放的data 里面的数据到底是什么???
如何根据一条sql映射到相应的hash_bucket ,其实就是如何找到 LIBRARY_OBJECT_HANDLE ???
我这边有一个现象,我两条相同的sql 映射到不同的 hash_bucket 上了
如下:
BUCKET 40886 total object count=1
BUCKET 41078:
LIBRARY OBJECT HANDLE: handle=671ef69c
name=select * from t
hash=45b47d9c timestamp=11-22-2007 11:04:49
namespace=CRSR flags=RON/TIM/PN0/SML/[12010000]
kkkk-dddd-llll=0000-0001-0001 lock=0 pin=0 latch#=1
lwt=671EF6B4[671EF6B4,671EF6B4] ltm=671EF6BC[671EF6BC,671EF6BC]
pwt=671EF6CC[671EF6CC,671EF6CC] ptm=671EF724[671EF724,671EF724]
ref=671EF6A4[671EF6A4, 671EF6A4] lnd=671EF730[671EF730,671EF730]
LIBRARY OBJECT: object=671ef424
type=CRSR flags=EXS[0001] pflags= [00] status=VALD load=0
CHILDREN: size=16
child# table reference handle
------ -------- --------- --------
0 671ef5e0 671ef340 671ee4ac
DATA BLOCKS:
data# heap pointer status pins change alloc(K) size(K)
----- -------- -------- ------ ---- ------ -------- --------
0 671ef62c 671ef4ac I/-/A 0 NONE 0.80 1.09
BUCKET 116770:
LIBRARY OBJECT HANDLE: handle=6740d778
name=select * from t
hash=f3f74e27 timestamp=11-22-2007 09:56:51
namespace=CRSR flags=RON/KGHP/TIM/PN0/SML/[12010000]
kkkk-dddd-llll=0000-0001-0001 lock=N pin=0 latch#=1
lwt=6740D790[6740D790,6740D790] ltm=6740D798[6740D798,6740D798]
pwt=6740D7A8[6740D7A8,6740D7A8] ptm=6740D800[6740D800,6740D800]
ref=6740D780[6740D780, 6740D780] lnd=6740D80C[6740D80C,6740D80C]
LIBRARY OBJECT: object=6740d500
type=CRSR flags=EXS[0001] pflags= [00] status=VALD load=0
CHILDREN: size=16
child# table reference handle
------ -------- --------- --------
0 6740d6bc 6740d41c 6740d1fc
1 6740d6bc 6740d45c 673e611c
DATA BLOCKS:
data# heap pointer status pins change alloc(K) size(K)
----- -------- -------- ------ ---- ------ -------- --------
0 6740d708 6740d588 I/P/A 0 NONE 0.86 1.09
?????
to: Yong Huang
Obviously, each library cache latch child can manage multiple SQLs.
从哪儿可以看出呢 ?
那oracle 如何区分多个
library cache latch呢 ? 不同的变量? 或者说是这个latch 是直接放在struct 中的 ?
这样的话就是每个sql 都有一个惟一的 latch ?
可否用相应的数据结构描述一下? 谢谢
Regarding your description of cache buffers chains and buckets, I always thought the concepts of buckets and chains are the same. Could it possible they actually are one struct instead of two?
这应该是两个struct ,hash_bucket 是用来快速定位的 ,因为是根据数组的下标来搜索的 ,那么得到一个地址很容易
数组首地址+偏移量找到相应的 bucket ,然后根据bucket 来访问 链表。