Linux で大きな SGA を実現するには?


  1. プロセスのメモリマップ
  2. SGA の拡張範囲
  3. SGA の拡張方法
    1. SGA の 再配置
    2. VLM を使った拡張バッファキャッシュ
  4. bigpages
  5. プロセスのレイアウト
  6. mapped_base を自動的に設定する

プロセスのメモリマップ

まず oracle プロセスのメモリマップを見てみましょう。 それは /proc/PID の下の maps にあります。

> uname -a 
Linux cometeor 2.4.9-e.24 #1 Tue May 27 16:15:51 EDT 2003 i686 unknown

> ps -ef|grep ora_|grep -v grep
ora920   32373     1  0 Dec15 ?        00:00:00 ora_pmon_ora920
ora920   32375     1  0 Dec15 ?        00:00:00 ora_dbw0_ora920
ora920   32377     1  0 Dec15 ?        00:00:00 ora_lgwr_ora920
ora920   32379     1  0 Dec15 ?        00:00:00 ora_ckpt_ora920
ora920   32381     1  0 Dec15 ?        00:00:03 ora_smon_ora920
ora920   32383     1  0 Dec15 ?        00:00:00 ora_reco_ora920

> cd /proc/32373
> ls -l
total 0
-r--r--r--    1 ora920   dba             0 12月 16 09:09 cmdline
lrwxrwxrwx    1 ora920   dba             0 12月 16 09:09 cwd -> /home/ora920/app/oracle/product/9.2.0/dbs
-r--------    1 ora920   dba             0 12月 16 09:09 environ
lrwxrwxrwx    1 ora920   dba             0 12月 16 09:09 exe -> /home/ora920/app/oracle/product/9.2.0/bin/oracle
dr-x------    2 ora920   dba             0 12月 16 09:09 fd
-rw-------    1 ora920   dba             0 12月 16 09:09 mapped_base
-r--r--r--    1 ora920   dba             0 12月 16 09:09 maps
-rw-------    1 ora920   dba             0 12月 16 09:09 mem
-rw-------    1 ora920   dba             0 12月 16 09:09 oom_priority
lrwxrwxrwx    1 ora920   dba             0 12月 16 09:09 root -> /
-r--r--r--    1 ora920   dba             0 12月 16 09:09 stat
-r--r--r--    1 ora920   dba             0 12月 16 09:09 statm
-r--r--r--    1 ora920   dba             0 12月 16 09:09 status

> cat mapped_base
1073741824                   (0x40000000)

> cat maps
08048000-0a4ba000 r-xp 00000000 03:41 4571224    /home/ora920/app/oracle/product/9.2.0/bin/oracle  <-- text segment
0a4ba000-0ad53000 rw-p 02471000 03:41 4571224    /home/ora920/app/oracle/product/9.2.0/bin/oracle  <-- data segment
0ad53000-0ade8000 rwxp 00000000 00:00 0          <-- BSS data segment
                                                 <-- mapped_base
40000000-40016000 r-xp 00000000 03:03 4734983    /lib/ld-2.2.4.so
40016000-40017000 rw-p 00015000 03:03 4734983    /lib/ld-2.2.4.so
40017000-40018000 rw-p 00000000 00:00 0
40018000-40019000 r-xp 00000000 03:41 4391054    /home/ora920/app/oracle/product/9.2.0/lib/libodmd9.so
40019000-4001a000 rw-p 00000000 03:41 4391054    /home/ora920/app/oracle/product/9.2.0/lib/libodmd9.so
4001a000-4001c000 r-xp 00000000 03:41 4390927    /home/ora920/app/oracle/product/9.2.0/lib/libskgxp9.so
4001c000-4001d000 rw-p 00001000 03:41 4390927    /home/ora920/app/oracle/product/9.2.0/lib/libskgxp9.so
4001d000-4001f000 r-xp 00000000 03:41 4391051    /home/ora920/app/oracle/product/9.2.0/lib/libskgxn9.so
4001f000-40020000 rw-p 00001000 03:41 4391051    /home/ora920/app/oracle/product/9.2.0/lib/libskgxn9.so
40020000-402ff000 r-xp 00000000 03:41 4391044    /home/ora920/app/oracle/product/9.2.0/lib/libjox9.so
402ff000-4040f000 rw-p 002de000 03:41 4391044    /home/ora920/app/oracle/product/9.2.0/lib/libjox9.so
4040f000-40411000 rw-p 00000000 00:00 0
40411000-40414000 r-xp 00000000 03:03 4734996    /lib/libdl-2.2.4.so
40414000-40415000 rw-p 00002000 03:03 4734996    /lib/libdl-2.2.4.so
40415000-40437000 r-xp 00000000 03:03 4734998    /lib/libm-2.2.4.so
40437000-40438000 rw-p 00021000 03:03 4734998    /lib/libm-2.2.4.so
40438000-40439000 rw-p 00000000 00:00 0
40439000-40448000 r-xp 00000000 03:03 4735028    /lib/libpthread-0.9.so
40448000-4044c000 rw-p 0000e000 03:03 4735028    /lib/libpthread-0.9.so
4044c000-4046c000 rw-p 00000000 00:00 0
4046c000-4047f000 r-xp 00000000 03:03 4735001    /lib/libnsl-2.2.4.so
4047f000-40480000 rw-p 00012000 03:03 4735001    /lib/libnsl-2.2.4.so
40480000-40482000 rw-p 00000000 00:00 0
40482000-405b7000 r-xp 00000000 03:03 4734992    /lib/libc-2.2.4.so
405b7000-405bc000 rw-p 00134000 03:03 4734992    /lib/libc-2.2.4.so
405bc000-405e4000 rw-p 00000000 00:00 0
405e4000-40664000 rw-p 00000000 03:03 70990      /dev/zero
40664000-4066e000 r-xp 00000000 03:03 4735017    /lib/libnss_files-2.2.4.so
4066e000-4066f000 rw-p 00009000 03:03 4735017    /lib/libnss_files-2.2.4.so
4066f000-406b0000 rw-p 00000000 00:00 0
50000000-56c00000 rw-s 00000000 00:04 229380     /SYSV9645cf3c           <--  SGA
56c00000-56c01000 r--s 06c00000 00:04 229380     /SYSV9645cf3c 
56c01000-56ca2000 rw-s 06c01000 00:04 229380     /SYSV9645cf3c
56ca2000-56ca3000 r--s 06ca2000 00:04 229380     /SYSV9645cf3c
56ca3000-57000000 rw-s 06ca3000 00:04 229380     /SYSV9645cf3c 
bffe4000-bffee000 rwxp ffff7000 00:00 0                                  <--  stack


上記マップの説明は以下のとおりです。

  1. 08048000-0a4ba000 が oracle プロセスのテキストセグメントです。
  2. 0a4ba000-0ad53000 が oracle プロセスのデータセグメントです。
  3. 0ad53000-0ade8000 が oracle プロセスの BSS (Block Started by Symbol) です。
  4. 40000000 が mapped_base です。
  5. 50000000-57000000 が SGA です。
  6. c0000000 より低いアドレスは stack です(通常 8MB)。
    ksh の ulimit -a で得られる stack のサイズに依存します。
  7. c0000000 以降は Linux kernel が使用します。

これから mapped_base より低いアドレス部分と SGA より高アドレス部分に空きがあることがわかります。
この未使用部分を SGA として利用するために再配置するのが、 はじめに説明する方法です。

またメモリの属性は以下のとおりです。(linux/ipc/shm.c remove_attach())

r read
w write
x execute
s share
p private

SGA の拡張範囲

RedHat Linux Advanced Server 2.1 (Enterprise kernel on IA-32) では物理アドレスを 36bit 扱えるので、最大 64GB までがアドレス可能です。 もちろん SGA を拡張しても、実装されているメモリ量が少なければ効果はありません。

上記以外のバージョンでは、32bit アドレス (4GB) ですので、 SGA の最大サイズは 2.7 GB (0xc0000000 - 0x15000000 = 0xa6000000) です。
正確には 2.7GB - stack サイズです。

そのうち mapped_base を変更しない場合は 1.7GB (0xc0000000 - 0x50000000 = 0x70000000) です。

SGA の拡張方法

SGA(共有メモリ)の拡張方法には、以下の2つがあります。

  1. SGA の 再配置

    通常のアーキテクチャでも可能な方法ですが、最大 2.7 GB までしか拡張できません。 それは 32bit アドレスでは 4GB までですので、 そのうちの SGA の最大は 2.7 GB です。 ただし、実メモリが 4GB ないと効果はありません。

    この方法は、oracle の実行モジュールの SGA のマップ開始アドレスをさげることで実現できます。

  2. VLM を使った buffer cache の拡張

    VLM (Very Large Memory) を使用すると最大 64GB まで拡張可能です。
    そこで SGA の一部である buffer cache (db_block_size * db_block_buffers) を VLM で実現します。 buffer cache のサイズがかなり大きくなりますので、 パフォーマンスの低下が問題になります。 その問題を回避するために、同時に bigpages の機能と設定方法も説明します。 ただし、この機能を利用するためには実メモリが充分あり、 しかも RedHat Linux Advanced Server 2.1 (Enterprise kernel) である必要があります。
    Enterprise kernel は Linux AS 2.1 2.4.9-e.25 以上 がインストールされていることをお薦めします。

    > uname -a                    システム名を表示
    > rpm -q kernel-enterprice    enterprice kernel がインストールされているかの確認
    > /sbin/sysctl -a             現在の kernel の設定値を表示
    

SGA の 再配置

  1. 共有メモリとして使用したい最大サイズを指定します。(2GB)

    # echo 2147483648 >/proc/sys/kernel/shmmax                現在値の変更
    # echo kernel.shmmax = 2147483648 >> /etc/sysctl.conf     reboot 後の変更の反映
    

  2. oracle のための OS セッションの mapped_base を小さくします。(0x10000000)
    尚この変更は、oracle ユーザとしてログインした場合は毎回する必要があります。 それは oracle を起動する親プロセスの mapped_base を oracle が引き継ぐためです。
    自動的にするためには、以下を参考にしてください。

    oracle ユーザとしてログインしたときのシェルの PID を求め、 その mapped_base を変更します。

    # echo 268435456 > /proc/$PID/mapped_base
    

  3. oracle 実行モジュールの SGA のマップ開始アドレスを小さくします。
    通常 0x50000000 ですが 0x15000000 にします。 これで 2.7GB (0x15000000 - 0xc0000000) まで SGA を拡張できます。

    > cd $ORACLE_HOME/rdbms/lib     
    > cp ksms.s ksms.s.org                   オリジナルをバックアップ
    > genksms -s 0x15000000 > ksms.s         SGA のマップ開始アドレスを変更
    > make -f ins_rdbms.mk ksms.o            オブジェクトを作成
    > make -f ins_rdbms.mk ioracle           oracle をリリンク
    

VLM を使った拡張バッファキャッシュ

SGA の一部であるバッファキャッシュ (db_block_size * db_block_buffers) の拡張を行うためには、以下の方法があります。

shmfs

shmfs はメモリをファイルシステムにマップしたもので、 バッファキャッシュを共用メモリのように扱う方法の1つです。

以下の手順で作成します。

  1. root ユーザで /dev/shm を作る

    > mkdir /dev/shm

  2. root ユーザで shmfs を mount する

    > mount -t shm shmfs -o size=3g /dev/shm

  3. /etc/fstab に shmfs の mount を追加(boot 時に自動マウントするため)

    > shm /dev/shm shmfs size=3g 0 0

bigpages

共有メモリのサイズを大きくした場合は、 それらを扱うために負荷がかかりますので、 メモリを扱う単位であるページを大きくするための bigpages を設定することをお薦めします。

Linux のデフォルトのページサイズは 4KB ですが、 そのサイズを大きくして (2MB or 4MB) OS が扱うページ数を少なくすると高効率となります (ページは OS がメモリを管理するための最小単位です)。

bigpage のページサイズは Linux 4GB kernel では 4MB で、 Linux 64GB kernel では 2MB です。 また bigpages を指定したメモリ部分は swapout されません。

shmfs を含む SGA を bigpages として扱うには、以下の設定が必要です。

  1. SGA を bigpages として扱いません。
  2. SGA を systemV ipc かつ bigpages として扱います。
  3. バッファキャッシュ (shmfs) を含む SGA を bigpages として扱います。

/etc/sysctl.conf に以下の設定を追加します。

kernel.shm-use-bigpages = 2

      0: SGA を bigpages として扱いません。
      1: SGA を systemV ipc かつ bigpages として扱います。
      2: バッファキャッシュ (shmfs) を含む SGA を bigpages として扱います。

確認は以下のとおりです。

cat /proc/sys/kernel/shm-use-bigpages

bigpages の設定

下記の3つの設定をした後、OS を reboot し、 oracle を再起動してください。

  1. /boot/grub/grub.conf で以下の設定を追加
    SGA または shmfs のうち bigpages として扱いたいサイズを指定
    
    
    bigpages=XXXMB
    

  2. /etc/sysctl.conf で以下の設定を追加
       
    1. バッファキャッシュ (shmfs) を含む SGA を bigpages として扱います。

       kernel.shm-use-bigpages = 2
      

    2. 共有メモリとして使用したい最大サイズを指定します。(1GB)

      ここで指定したサイズは、これを超えた場合エラーとなる最大数で、 それ以外に影響はありません。 ただし shmfs の部分は含みません。

       kernel.shmmax = 1073741824
      
  3. init.ora に拡張バッファキャッシュ機能を使う設定

     use_indirect_data_buffer=true
    

    上記設定をした場合は動的キャッシュ・パラメータ (DB_CACHE_SIZE, DB_xxK_CACHE_SIZE)を設定してはならず、 DB_BLOCK_BUFFERS パラメータを使用してデータベース・キャッシュ・ サイズを指定するようにご注意ください。)

bigpages の設定の確認方法

cat /proc/<PID>/maps

# cat /proc/pmon_PID/maps    (抜粋)

405c7000-421c7000 rw-b 00000000 00:09 15920 /dev/shm/orashm_7471112
421c7000-42247000 rw-p 00000000 08:41 38713 /dev/zero
42247000-42251000 r-xp 00000000 08:41 475273/lib/libnss_files-2.2.4.so
42251000-42252000 rw-p 00009000 08:41 475273/lib/libnss_files-2.2.4.so
42252000-42293000 rw-p 00000000 00:00 0
50000000-50400000 rw-B 00000000 00:04 7340036    /SYSV00000000 (deleted)
51000000-52400000 rw-B 00000000 00:04 7372805    /SYSV00000000 (deleted)
53000000-54800000 rw-B 00000000 00:04 7405574    /SYSV00000000 (deleted)
55000000-56c00000 rw-B 00000000 00:04 7438343    /SYSVbfd9a630 (deleted)

上記の結果で、rw-B または rw-b の部分が bigpages であることを示します。

注意点

oracle を shutdown し、以下の設定後再起動すると設定が反映されますが、 OS reboot 後は reset されますので、上記の方法でシステムを設定してください。

下記の方法は、動的にシステムを変更する場合に有効です。

上記 1 に関しましては、以下の方法もあります。

1. /etc/lilo.conf
bigpages=XXXMB     ...... bigpages のサイズを指定

上記 2 に関しましては、以下の方法もあります。

2.1 # echo 2 > /proc/sys/kernel/shm-use-bigpages
 バッファキャッシュを shmfs かつ bigpages として扱います。

2.2 # echo 4000000000 >/proc/sys/kernel/shmmax
  共有メモリとして使用したい最大サイズを指定します。(4GB)

プロセスのレイアウト

デフォルト設定では、物理メモリは 0xC0000000 番地にストレートマッピングされる。

Linuxカーネルは物理メモリの先頭にロードされているため、 仮想空間上は0xC0000000番地に割付いているように見える。 Linuxカーネル空間は多重仮想化せず、このままの状態で動作する。

プロセスの空間は、0x0〜0xC0000000の間に、動的に生成される。 空間は各プロセス毎に独立した多重仮想として生成する。 仮想空間上では有効なページであっても、 そのページに物理ページが割り当てられてるとは限らない。 ディスク上のファイルのブロックや SWAPを指していたり、 実際にアクセスがあったときに0クリアした メモリを割り当てる指定になっていることもある。

mapped_base を自動的に設定する

以下の lowermap.c のプログラムを作成し、モードを変更後、 ログイン時に実行するようにします。

> make lowermap
# chmod u+s lowermap
# chown root lowermap
# chgrp bin lowermap
# cp lowermap /usr/local/bin

[ lowermap.c ]

/*-----------------------------------------------------------------------
 * lowermap.c
 * suid program to lower the mapped_base shared memory addr
 * Authors: Robert Hentosh, robert_hentosh@dell.com
 * Zafar Mahmood, zafar_mahmood@dell.com
 *
 * Copyright 2002 Dell Computer Corp. Distributed under the GPL
 *
 * # chmod u+s lowermap
 * # chown root lowermap
 * # chgrp bin lowermap
  ----------------------------------------------------------------------*/

#include 
#include 
#include 
#include 
#include 
#include 

#define NEW_MAPPED_BASE     "0x10000000"
#define NEW_SHMMAX          "0xB2D05E00"
#define PROGNAME            "lowermap"

int main( int argc, char *argv[] ) {
   pid_t ppid;
   char fname[256];
   int fd,ret;
   int retval;

/** should ignore signals in setuid() programs **/
   signal( SIGCHLD, SIG_IGN );
   signal( SIGCONT, SIG_IGN );
   signal( SIGTSTP, SIG_IGN );
   signal( SIGTTIN, SIG_IGN );
   signal( SIGTTOU, SIG_IGN );
   ret = setuid(0);
#if 0
   printf("setuid  ret=%d\n",ret);
#endif
   if (ret)   perror( PROGNAME );
   ppid = getppid();

   sprintf( fname, "/proc/%u/mapped_base", ppid );
   fd = open( fname, O_WRONLY );
   if ( fd == -1 ) {
/* possibly not on Red Hat Enterprise Linux */
      fprintf( stderr, "Unable to open file: %s\n", fname );
      perror( PROGNAME );
      exit errno;
   }

   retval = write( fd, NEW_MAPPED_BASE, sizeof( NEW_MAPPED_BASE ) );
   if ( retval == -1 ) {
/* possibly lowermap is not setuid root */
      fprintf( stderr, "Unable to modify mapped base.\n");
      fprintf( stderr, "Verify program permissions and ownership.\n");
      perror( PROGNAME );
      exit errno;
   }

   close( fd );
   printf( "Lowering mapped base for pid:%u to %s\n", ppid, NEW_MAPPED_BASE );

   fd = open( "/proc/sys/kernel/shmmax", O_WRONLY );
   if ( fd == -1 ) {
      fprintf(stderr, "Unable to modify maximum shared memory segment size.\n");
      perror( PROGNAME );
      exit errno;
   }

   retval = write( fd, NEW_SHMMAX, sizeof( NEW_SHMMAX ) );
   if ( retval == -1 ) {
      fprintf(stderr, "Unable to modify maximum shared memory segment size.\n");
      perror( PROGNAME );
      exit errno;
   }

   close( fd );
   printf( "Increased maximum shared memory segment size to %u\n",
   NEW_SHMMAX );

   exit (0);
}