Oracle中的共享内存区的分配都是以chunk为最小单位的。Chunk不是一个固定值,它是一个扩展段(extent)中一块连续的内存。而Oracle的内存(其他存储,如磁盘也是)的增长是以扩展段为基础的。
空闲的chunk按照大小来组织在相应的空闲列表(Free List)中。而未pin住的、可重建(unpinned recreatable)的chuck被维护在两个分别用于周期性chunk和短期chunk的LRU链表中。子堆还有一个包含少许空闲内存的主永久内存chunk。
空闲内存都是由空闲列表(free list)统一管理、分配的。每个空闲的chunk(大块)都会属于也只属于一个空闲列表。空闲列表上的chunk的大小范围是由bucket来划分的。Bucket直译为“桶”,在西方,往往用桶来盛装一定品质范围的物品,以便查找。比如,在采矿时,用不同的桶来装不同纯度的矿石,在桶上标明矿石的纯度范围,以便在提炼时可以采用不同工艺。在这里,我们也可以把bucket视为一种索引,使Oracle在查找空闲块时,先定位所需的空闲块在哪个bucket的范围内,然后在相应的空闲列表中查找。
一次内存的分配过程如下:当一个进程需要一个内存的大块(chunk)时,它会先扫描目标空闲列表(每个空闲列表对应有一个bucket,bucket是这个空闲列表的中chunk的大小范围)以查找最适合大小的chunk。如果找不到一个大小正好合适的chunk,则继续扫描空闲列表中更大的chunk。如果找到的可用chunk比所需的大小大24字节或者更多,则这个chunk就会被分裂,剩余的空闲chunk又被加到空闲列表的合适位置。如果空闲列表中没有一个chunk能满足要求的大小,则会从非空的相邻bucket的空闲列表中取最小的chunk。如果所有空闲列表都是空的,就需要扫描LRU链表释放最近最少使用的内存。当chunk空闲时,如果相邻的chunk也是空闲的,它们可能会结合(coalesce)起来。
当所有空闲列表都没有合适的空闲chunk可用时,就开始扫描LRU链表,将最近最少使用的chunk释放掉(如有空闲的相邻chunk,则结合),放到相应的空闲列表种去,直到找到合适的chunk或者达到最大查找数限制。如果在释放了LRU链表中最近最少使用的chunk后没没有找到合适空闲chunk,系统就抛4031错误。
如果找到了可用空闲chunk,就将它从空闲列表中移出,放到LRU链表中去。
下面介绍一下Shared Pool的分配和回收。
在Shared Pool中,空闲列表扫描、管理和chunk分配的操作都是受到shared pool latch保护的。显然,如果shared pool含有大量的非常小的空闲chunk,则扫描空闲列表时间将很长,而shared pool latch则会要保持很久。这就是导致shared pool latch争用的主要原因了。有些DBA通过增加shared pool来减少shared pool latch争用,这种方法是没有用的,可能反倒会加剧争用(作为短期解决方法,可以flush shared pool;而要真正解决问题,可以采取在实例启动时keep住那些可能会断断续续使用的对象【这种对象最容易导致shared pool碎片】)。
只有执行了ALTER SYSTEM FLUSH SHARED_POOL才会使shared pool的空闲chunk全结合起来。因此,即使shared pool空闲内存之和足够大,也可能出现内存请求失败(空闲内存都是一些很小的碎片chunk)。
实际上,oracle实例启动时,会保留大概一半的Shared Pool,当有内存压力时逐渐释放它们。Oracle通过这种方法限制碎片的产生。Oracle的少量空袭内存(spare free memory)和X$表即其他持久内存结构一起,隐含在shared pool的主要持久内存chunk中。这些内存不在shared pool的空闲列表中,因此能够立即被分配。但是,却包含在视图V$SGASTAT的free memory的统计数据中。
SQL> select * from v$sgastat
2 where pool = 'shared pool'
3 and name = 'free memory';
POOL NAME BYTES
------------ -------------------------- ----------
shared pool free memory 18334468
SQL>
而spare free memory可以用以下方式查出:
select avg(v.value) shared_pool_size, greatest(avg(s.ksmsslen) - sum(p.ksmchsiz), 0) spare_free, to_char( 100 * greatest(avg(s.ksmsslen) - sum(p.ksmchsiz), 0) / avg(v.value), '99999' ) || '%' wastagefrom sys.x$ksmss s, sys.x$ksmsp p, sys.v$parameter vwhere s.inst_id = userenv('Instance') and p.inst_id = userenv('Instance') and p.ksmchcom = 'free memory' and s.ksmssnam = 'free memory' and v.name = 'shared_pool_size';
注意:如果10g中用了SGA内存自动管理。以上语句可能无法查出。
当需要时,少量的空闲内存chunk会被释放到shared pool中。除非所有这些少量空闲内存倍耗尽了,否则不会报4031错误。如果实例在负载高峰运行了一段时期之后还有大量的少量空闲内存,这就说明shared pool太大了。
而未pin住的、可重建(unpinned recreatable)的chuck被维护在两个分别用于周期性chunk和短期chunk的LRU链表中。这两个LRU链表的长度可以通过表X$KGHLU查到,同时还能查到被flush的chunk数、由于pin和unpin而加到和从LRU链表中移出的chunk数。X$KGHLU还能显示LRU链表没有被成功flush的次数,以及最近一次这样的请求失败的请求大小。
SQL> column
kghlurcr heading "RECURRENT|CHUNKS"
SQL> column kghlutrn heading "TRANSIENT|CHUNKS"
SQL> column kghlufsh heading "FLUSHED|CHUNKS"
SQL> column kghluops heading "PINS AND|RELEASES"
SQL> column kghlunfu heading "ORA-4031|ERRORS"
SQL> column kghlunfs heading "LAST ERROR|SIZE"
SQL> select
2 kghlurcr "RECURRENT_CHUNKS",
3 kghlutrn "TRANSIENT_CHUNKS",
4 kghlufsh "FLUSHED_CHUNKS",
5 kghluops "PINS AND_RELEASES",
6 kghlunfu "ORA-4031_ERRORS",
7 kghlunfs "LAST ERROR_SIZE"
8 from
9 sys.x$kghlu
10 where
11 inst_id = userenv('Instance');
RECURRENT_CHUNKS TRANSIENT_CHUNKS FLUSHED_CHUNKS PINS AND_RELEASES ORA-4031_ERRORS LAST ERROR_SIZE
---------------- ---------------- -------------- ----------------- --------------- ---------------
327 368 0 7965 0 0
865 963 2960 102138 0 0
1473 5657 96 20546 1 540
0 0 0 0 0 0
如果短期链表的长度大于周期链表的长度3倍以上,说明Shared Pool太大,如果chunk flash对LRU 操作的比例大于1/20,则说明Shared Pool可能太小。
在UNIX下,Oracle是以多进程的方式运行的。除了几个后台进程外,Oracle为每个连接起一个进程(进程名称中,LOCAL = NO)。而UNIX下的内存分为两类,一种叫共享内存,即可以被多个进程共享;还有一种就是私有进程,只能被所分配的进程访问。更加Oracle内存区的不同特性,SGA内存可以被所有会话进程共享,因此是从共享内存中分配的;而PGA是进程私有的,因而是从私有内存中分配的。
在Unix下,Oracle的SGA是保存在共享内存中的(因为共享内存是可以被多个进程共享的,而SGA正是需要能被所有Oracle进程所共享)。因此,系统中必须要有足够的共享内存用于分配SGA。而信号量则是在共享内存被多个进程共享时,防止发生内存冲突的一种锁机制。
UNIX下,对于内存的管理配置,是由许多内核参数控制,在安装使用Oracle时,这些参数一定要配置正确,否则可能导致严重的性能问题,甚至Oracle无法安装、启动。涉及共享内存段和信号量的参数:
参数名称 |
建议大小(各个平台的Oracle建议值可以去metalink上找) |
参数描述 |
SHMMAX |
可以得到的物理内存 (0.5*物理内存) |
定义了单个共享内存段能分配的最大数. SHMMAX 必须足够大,以在一个共享内存段中能足够分配Oracle的SGA空间.这个参数设置过低会导致创建多个共享内存段,这会导致Oracle性能降低. |
SHMMIN |
|
定义了单个共享内存段能分配的最小数. |
SHMMNI |
512 |
定义了整个系统共享内存段的最大数。 |
NPROC |
4096 |
系统的进程总数 |
SHMSEG |
32 |
定义了一个进程能获取的共享内存段的最大数. |
SEMMNS |
(NPROC * 2) * 2 |
设置系统中的信号数. SEMMNS的默认值是128 |
SEMMNI |
(NPROC * 2) |
定义了系统中信号集的最大数 |
SEMMSL |
与Oracle中参数processes大小相同 |
一个信号集中的信号最大数 |
SEMMAP |
((NPROC * 2) + 2) |
定义了信号映射入口最大数 |
SEMMNU |
(NPROC - 4) |
定义了信号回滚结构数 |
SEMVMX |
32768 |
定义了信号的最大值 |
信号量(Semaphore):对于每个相关的process都給予一个信号來表示其目前的狀态。主要的目地在于确保process间能同步,避免process存取shared data时产生碰撞( collisions)的情況。可以把信号量视为操作系统级的用于管理共享内存的钥匙,每个进程需要有一把,一个信号量集就是一组钥匙,这组钥匙可以打开共同一个共享内存段上的所。当一个进程需要从共享内存段中获取共享数据时,使用它自己的钥匙打开锁,进入后,再反锁以防止其他进程进来。使用完毕后,将锁释放,这样其他进程就可以存取共享数据了。
共享内存(Shared Memory)是指同一块记内存段被一个以上的进程所共享。这是我们所知速度最快的进程间通讯方式。使用共享内存在使用多CPU的机器上,会使机器发挥较佳的效能。
可以通过以下命令检查你当前的共享内存和信号量的设置:
$ sysdef | more
当Oracle异常中止,如果怀疑共享内存没有被释放,可以用以下命令查看:
$ipcs -mop
IPC status from /dev/kmem as of Thu Jul 6 14:41:43 2006
T ID KEY MODE OWNER GROUP NATTCH CPID LPID
Shared Memory:
m 0 0x411c29d6 --rw-rw-rw- root root 0 899 899
m 1 0x4e0c0002 --rw-rw-rw- root root 2 899 901
m 2 0x4120007a --rw-rw-rw- root root 2 899 901
m 458755 0x0c6629c9 --rw-r----- root sys 2 9113 17065
m 4 0x06347849 --rw-rw-rw- root root 1 1661 9150
m 65541 0xffffffff --rw-r--r-- root root 0 1659 1659
m 524294 0x5e100011 --rw------- root root 1 1811 1811
m 851975 0x5fe48aa4 --rw-r----- oracle oinstall 66 2017 25076
然后它ID号清除共享内存段:
$ipcrm –m 851975
对于信号量,可以用以下命令查看:
$ ipcs -sop
IPC status from /dev/kmem as of Thu Jul 6 14:44:16 2006
T ID KEY MODE OWNER GROUP
Semaphores:
s 0 0x4f1c0139 --ra------- root root
... ...
s 14 0x6c200ad8 --ra-ra-ra- root root
s 15 0x6d200ad8 --ra-ra-ra- root root
s 16 0x6f200ad8 --ra-ra-ra- root root
s 17 0xffffffff --ra-r--r-- root root
s 18 0x410c05c7 --ra-ra-ra- root root
s 19 0x00446f6e --ra-r--r-- root root
s 20 0x00446f6d --ra-r--r-- root root
s 21 0x00000001 --ra-ra-ra- root root
s 45078 0x67e72b58 --ra-r----- oracle oinstall
当Oracle异常中止,可以根据信号量ID,用以下命令清除信号量:
$ipcrm -s 45078
一个共享内存段可以可以分别由以下两个属性来定位:
o Key:一个32位的整数
o 共享内存ID:系统分配的一个内存
$ ipcs -mop
IPC status from /dev/kmem as of Thu Jul 6 10:32:12 2006
T ID KEY MODE OWNER GROUP NATTCH CPID LPID
Shared Memory:
m 0 0x411c29d6 --rw-rw-rw- root root 0 899 899
m 1 0x4e0c0002 --rw-rw-rw- root root 2 899 901
m 2 0x4120007a --rw-rw-rw- root root 2 899 901
m 458755 0x0c6629c9 --rw-r----- root sys 2 9113 17065
m 4 0x06347849 --rw-rw-rw- root root 1 1661 9150
m 65541 0xffffffff --rw-r--r-- root root 0 1659 1659
m 524294 0x5e100011 --rw------- root root 1 1811 1811
m 851975 0x5fe48aa4 --rw-r----- oracle oinstall 66 2017 25076
对于PGA,由于是分配的私有内存,不存在争用问题,因而OS也没有相应的信号量来控制(同理,PGA中也没有latch)。在10g之前,PGA的私有内存是通过函数malloc()和sbrk()进行分配扩展的,10g之后,私有内存是通过函mmap()进行初始化的。
另外,还有一点要注意的是,和SGA不同,PGA内存的大小不是固定的,是可以扩展的(前者的大小是固定的,无法增长的)。因而进程的私有内存是会增长的。因此,一个规划好的系统发生内存不足的情况通常是由于进程的私有内存或进程数量突然增长造成的(PGA_AGGREGATE_TARGET参数能尽量控制但不保证PGA内存总量被控制在一定数值范围内)。
隐含参数_pga_large_extent_size(1048576)和_uga_cga_large_extent_size(262144)就控制了当初始化时,PGA、UGA和CGA扩张段的最大大小。
swap(交换)区,是UNIX中用来分配虚拟内存的一块特殊的磁盘分区。UNIX启动每一个进程,都需要在swap区预留一块和内存一样大小的区域,以防内存不够时作数据交换。当预留的swap区用完时,系统就不能再启动新的进程。比如,系统物理内存是4G,而设置的交换区只有1G,那么可以计算得出大概3G的内存会浪费(Buffer Cache除外,可能有2G浪费)。
在HP-UX中,参数swapmen_on可以让系统创建一个pseudo-swap(伪交换区),大小为系统物理内存的3/4,但是这个伪交换区并不占用任何内存和硬盘资源。只是说,让系统认为,交换区的大小是1+4*3/4=4G,而不是1G,就是说可以启动更多的进程,避免内存的浪费。
一般系统物理内存不大的时候,设置交换区是物理内存的2-4倍,swapmen_on设置为1或0都没什么影响,但是当系统内存很大如8G时,因为swap一般不设为16G-32G,这时开启swapmen_on就很必要了。
hp建议,即使设置了swapmen_on,也将你的swap为物理内存的1-1.5倍。
swap大小设置不当,也同样会造成系统的性能问题。因为,swap中首先会为各个进程留出一个保留区,这部分区去掉后,swap的可用大小就比较小了(这就是为什么用swapinfo可能看到Total PCT USED为100%而dev PCT USED为0%)。当swap可用区不足,而由内存需要被page out到swap区中,就需要先将swap区中一些页被page in到物理内存中去,因而导致发生交换,产生性能问题。
swap的使用情况可以通过swapinfo查看:
> swapinfo -mt
Mb Mb Mb PCT START/ Mb
TYPE AVAIL USED FREE USED LIMIT RESERVE PRI NAME
dev 4096 0 4096 0% 0 - 1 /dev/vg00/lvol2
dev 8000 0 8000 0% 0 - 1 /dev/vg00/swap2
reserve - 12026 -12026
memory 20468 13387 7081 65%
total 32564 25413 7151 78% - 0 -
转载自:http://www.hellodba.com/reader.php?ID=108&lang=CN