32bit oracle由于位数限制,使得oracle进程只能访问4g(2的32次方)以下的虚拟内存地址,在很多时候这是一个很让人头疼的问题,因为空着许多内存而不能使用,而默认情况下SGA不能超过1.7g。比如我们的linux下有8g内存,却有部分空着不能用干着急。这个时候我们就要考虑怎样扩展oracle的SGA。那么首先,如何识别32bit的oracle呢?我们可以通过如下查询得到
sys@OCN> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
PL/SQL Release 9.2.0.4.0 - Production
CORE 9.2.0.3.0 Production
TNS for Linux: Version 9.2.0.4.0 - Production
NLSRTL Version 9.2.0.4.0 - Production
如果是64bit oracle,在查询结果中一定会显示 64bit 字样,没有出现,则一定是32bit oracle .当然,在os上通过file oracle 也能看到
[oracle@ocn2 bin]$ cd $ORACLE_HOME/bin
[oracle@ocn2 bin]$ file oracle
oracle: setuid setgid ELF 32-bit LSB executable, Intel 80386, version 1, dynamically linked (uses shared libs), not stripped
[oracle@ocn2 bin]$
在某些os上,比如AIX上,64bit oracle 会正常显示信息,32bit则不正常显示。
在确认了32bit oracle之后,我们要明白,通常情况下我们的OS进程只能访问4g以下的空间,redhat linux AS 2.1 或者AS3.0版本例外,他们可以提供VLM(Very large memory)功能支持,使得通过转换可以使用36bit来标志内存地址,那么就是2的36次方理论上最大可支持64g内存访问。在oracle中,则是通过将内存当作文件来访问的,虚拟一个 /dev/shm 的文件系统,这个文件系统是完全由内存组成的,这样将突破4g的限制。那回过来我们看看,既然进程可以访问4g以下内存为何通常SGA又是1.7g呢。
在OS中,规定了一个进程在应用程序中,能访问的虚拟内存空间为0--3g,而3g--4g这段虚拟地址空间是保留给kernel使用的。要注意我们这里强调的是虚拟地址空间,并没有说物理地址空间,也就是说,假设有8g的内存,这0--3g的虚拟地址空间可能出现在8g内存的3g--8g部分内存段,并不是说是物理内存的0--3g段。而在这0--3g的虚拟地址中,oracle又是如何来使用的呢,这是固定好了地址的。
+++++++++++++++ 4g
+ -------------------------+
+------------------------- +
+------------------------- +
+++++++++++++++ 3g: kernel
+ ------------------------- +
+ ------------------------- +
+------------------------- +
+++++++++++++++ 2g: process stack
+ -------------------------+
+------------------------- +
+++++++++++++++ 1.25g: SGA起点
+++++++++++++++ 1g: oracle 共享库装载起点
+ -------------------------+
+ -------------------------+
+ -------------------------+
+++++++++++++++ 0g: oracle program(可执行代码)装载起点
在这段虚拟地址的分配中,1.25g是sga的起点,而进程的私有空间的分配(stack部分)却是从靠近3g处开始的。也就是实际上SGA和进程私有空间是共用了1.25g---3g这部分的,由于进程私有空间特别小,通常我们习惯性地认为SGA可以达到1.7g。进程私有空间有0.05g足够了。从oracle一启动开始,或者从任何一用户进程登陆开始,所有虚拟地址的分配都已经固定好了,只有用户私有空间还可以扩展。我们来看一下数据库启动后pmon进程(实际上任何一个进程都是一样的)的虚拟地址分配情况。由于我一台机器上跑着2个数据库,所以我们看其中一个,先看看数据库SGA相关信息
[root@ocnsb1 root]# su - oracle
[oracle@ocnsb1 oracle]$ sqlplus "/ as sysdba"
SQL*Plus: Release 9.2.0.4.0 - Production on Mon Jul 26 11:37:23 2004
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
With the Partitioning, Real Application Clusters, OLAP and Oracle Data Mining options
JServer Release 9.2.0.4.0 - Production
select">sys@OCN>select INSTANCE_NAME from v$instance ;
INSTANCE_NAME
----------------
roocn1
show">sys@OCN>show sga
Total System Global Area 437327188 bytes
Fixed Size 451924 bytes
Variable Size 301989888 bytes
Database Buffers 134217728 bytes
Redo Buffers 667648 bytes
sys@OCN>
exit">sys@OCN>exit
Disconnected from Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
With the Partitioning, Real Application Clusters, OLAP and Oracle Data Mining options
JServer Release 9.2.0.4.0 - Production
[oracle@ocnsb1 oracle]$ ipcs
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x73a32bdc 131072 oracle 640 457179136 50
0x84cc76ac 163841 oracle 640 1379926016 90
------ Semaphore Arrays --------
key semid owner perms nsems status
0x8df96364 622592 oracle 640 64
0x53609d64 753665 oracle 640 504
------ Message Queues --------
key msqid owner perms used-bytes messages
[oracle@ocnsb1 oracle]$
我这里的共享内存段只有一个,并且就是SGA的大小(shmid为131072)。这是因为shnmax设置太大的缘故
[oracle@ocn2 kernel]$ more /proc/sys/kernel/shmmax
3221225472
[oracle@ocn2 kernel]$
接下来我们看看PMON信息,首先要找到pmon进程号,然后去 /proc/pid/maps 中看该进程的虚拟地址分配信息
[oracle@ocnsb1 oracle]$ ps -ef|grep pmon
oracle 13655 1 0 Jul24 ? 00:00:00 ora_pmon_roocn1
oracle 13926 1 0 Jul24 ? 00:00:00 ora_pmon_ocn1
oracle 31435 31092 0 11:51 pts/3 00:00:00 grep pmon
[oracle@ocnsb1 oracle]$
[oracle@ocnsb1 oracle]$ more /proc/13655/maps
08048000-0a4ba000 r-xp 00000000 08:05 681621 /opt/oracle/products/9.2.0/bin/oracle
0a4ba000-0ad54000 rw-p 02471000 08:05 681621 /opt/oracle/products/9.2.0/bin/oracle
0ad54000-0ae07000 rwxp 00000000 00:00 0
这部分是oracle program装载信息,我们可以看到空间使用了0--0ae07000 ,这部分大小不足256MB
40000000-40016000 r-xp 00000000 08:02 448102 /lib/ld-2.2.4.so
这是oracle 共享库装载的起点,0x40000000 正好是1g
40016000-40017000 rw-p 00015000 08:02 448102 /lib/ld-2.2.4.so
40017000-40018000 rw-p 00000000 00:00 0
40018000-40019000 r-xp 00000000 08:05 308464 /opt/oracle/products/9.2.0/lib/libodmd9.so
40019000-4001a000 rw-p 00000000 08:05 308464 /opt/oracle/products/9.2.0/lib/libodmd9.so
4001a000-40026000 r-xp 00000000 08:05 308345 /opt/oracle/products/9.2.0/lib/libskgxp9.so
40026000-4002a000 rw-p 0000b000 08:05 308345 /opt/oracle/products/9.2.0/lib/libskgxp9.so
4002a000-40038000 r-xp 00000000 08:05 308461 /opt/oracle/products/9.2.0/lib/libskgxn9.so
40038000-40039000 rw-p 0000d000 08:05 308461 /opt/oracle/products/9.2.0/lib/libskgxn9.so
40039000-4004d000 rw-p 00000000 00:00 0
4004d000-4032c000 r-xp 00000000 08:05 308455 /opt/oracle/products/9.2.0/lib/libjox9.so
4032c000-4043c000 rw-p 002de000 08:05 308455 /opt/oracle/products/9.2.0/lib/libjox9.so
4043c000-4043e000 rw-p 00000000 00:00 0
4043e000-40441000 r-xp 00000000 08:02 448115 /lib/libdl-2.2.4.so
40441000-40442000 rw-p 00002000 08:02 448115 /lib/libdl-2.2.4.so
40442000-40443000 rw-p 00000000 00:00 0
40443000-40465000 r-xp 00000000 08:02 448117 /lib/libm-2.2.4.so
40465000-40466000 rw-p 00021000 08:02 448117 /lib/libm-2.2.4.so
40466000-40475000 r-xp 00000000 08:02 448147 /lib/libpthread-0.9.so
40475000-4047d000 rw-p 0000e000 08:02 448147 /lib/libpthread-0.9.so
4047d000-40490000 r-xp 00000000 08:02 448120 /lib/libnsl-2.2.4.so
40490000-40491000 rw-p 00012000 08:02 448120 /lib/libnsl-2.2.4.so
40491000-40493000 rw-p 00000000 00:00 0
40493000-40494000 r-xp 00000000 08:02 352330 /usr/lib/libaio.so.1
40494000-40495000 rw-p 00000000 08:02 352330 /usr/lib/libaio.so.1
40495000-405ca000 r-xp 00000000 08:02 448111 /lib/libc-2.2.4.so
405ca000-405cf000 rw-p 00134000 08:02 448111 /lib/libc-2.2.4.so
405cf000-405d3000 rw-p 00000000 00:00 0
405d3000-405d4000 r-xp 00000000 08:02 146106 /lib/libredhat-kernel.so.1.0.1
405d4000-405d5000 rw-p 00000000 08:02 146106 /lib/libredhat-kernel.so.1.0.1
405d5000-405f9000 rw-p 00000000 00:00 0
405fa000-40604000 r-xp 00000000 08:02 448136 /lib/libnss_files-2.2.4.so
40604000-40605000 rw-p 00009000 08:02 448136 /lib/libnss_files-2.2.4.so
40605000-40685000 rw-p 00000000 08:02 69445 /dev/zero
40685000-406c6000 rw-p 00000000 00:00 0
共享库消耗了不到20MB的空间
50000000-6b000000 rw-s 00000000 00:04 131072 /SYSV73a32bdc (deleted)
这是SGA的起点,0x50000000 表示1.25g
6b000000-6b001000 r--s 1b000000 00:04 131072 /SYSV73a32bdc (deleted)
6b001000-6b0a2000 rw-s 1b001000 00:04 131072 /SYSV73a32bdc (deleted)
6b0a2000-6b0a3000 r--s 1b0a2000 00:04 131072 /SYSV73a32bdc (deleted)
6b0a3000-6b400000 rw-s 1b0a3000 00:04 131072 /SYSV73a32bdc (deleted)
sga虚拟空间分配到这里,通过计算16进制数,正好和我们的SGA大小吻合,131072就是我们在ipcs查看的时候的 shmid
bffe5000-bffee000 rwxp ffff8000 00:00 0
bfff0000-bfff1000 r-xs 00000000 08:02 69304 /dev/vsys
由于0xc0000000正好是3g(16进制数c=12,4*3=12,0x40000000表示1g),则这里表示进程私��空间的分配的起点。查看oracle任何一个用户登陆进程也将发现这样的虚拟地址分配。在这里我们很容易看出来,oracle program 和共享内存库所占用的空间很小,没有必要给那么大,实际上,oracle program 给256M足够安全,而共享库给50M也足够安全了,也就是从理论上来讲,我们可以把oracle program所需要压缩在0x10000000以下,共享库所需要内存压缩在0x12000000以下,这样SGA的起点就可以提升到0x12000000(0.3g)。而原来是从0x5000000(1.25g)开始的,只有大约1.7g分配给SGA,现在从0.3g开始分配SGA则可以接近2.7g,比如分配2.65g内存给SGA。要实现这个功能,我们需要重新编译oracle program,降低共享库虚拟内存分配的地址和SGA的分配起点位置。0x4000000这个共享库装载的起点,是由进程的mapped_base来决定的
[oracle@ocnsb1 oracle]$ more /proc/13655/mapped_base
1073741824
这个大小是1G,则意味着共享库的装载从虚拟地址的1g位置开始,如果要降低这个地址,需要在oracle启动之前,也就是用root用户把将启动oracle的进程的mapped_base降低到256M,这样oracle启动之后的产生的进程都将继承这个值。
su - root echo 268435456 > /proc//mapped_base
当然我们也可以通过一些shell来实现oracle用户登陆之后自动降低mapped_base的功能,这个在google上就能找到了,或者参考
SGA起点从1.25g降低到0.3g则需要重新编译oracle program。必须要强调的是,SGA的起点是和共享库的起点mapped_ase相关的,SGA的起点至少得大于共享库的起点0.05g以上才是安全的,否则数据库将不能启动或者崩溃。
关闭oracle
su - oracle
cd $ORACLE_HOME/rdbms/lib
修改共享库装载地址的文件定义
genksms -s 0x12000000 > ksms.s
编译好目标文件
make -f ins_rdbms.mk ksms.o
重新编译oracle可执行文件
make -f ins_rdbms.mk ioracle
至于 redhat linux AS 2.1以上版本 oracle的VLM的使用则也是比较简单的了,参考
http://www1.ap.dell.com/content/ ... &l=zh&s=bsd
http://otn.oracle.com/global/cn/pub/notes/technote_rhel3.html
当然,internet上还有更多文章可以供参考。
在这里我要指出一个问题,也是我们在实践中遇到的一个问题,那就是,若SGA分配的很大,但没有使用VLM,几乎很靠近3g的时候,大约只留下20m左右。这样当一个进程进行hash join,由于我们的pga_aggregate_target设置为1g,oracle默认单个进程使用PGA可以达到pga_aggregate_target * 5% = 50M,则使得在进行hash join的时候出错
ORA-04030: out of process memory when trying to allocate 254476 bytes (hash-join subh,kllcqas:kllsltba)
我们调整pga_aggregate_target减小到400M则该查询执行成功。因为没有使用VLM的情况下单个进程的内存分配空间必须在3g以下,而PGA的分配也属于这个范畴。如果使用VLM则PGA已经被分配到4g以上部分的虚拟地址,不再有这个问题。在此不再对VLM进行过多的阐述,因为使用也比较简单,从原理上来讲就是通过os扩展32bit 到36bit,oracle使用文件来管理内存,并支持进程访问4g以上部分的虚拟内存。linux上这种用法得到推广的根本原因是因为其64bit oracle很少被使用,其他如sunOS/hp unix/AIX 等都广泛使用64bit oracle了,这些方法也就失去价值了。