2009년 3월 18일 수요일

ASSM

출처 : http://wiki.ex-em.com/index.php/ASSM 


ASSM

ASSM(Automatic Segment Space Management)은 FLM과 달리 프리리스트를 사용해 프리 블록을 관리하지 않는다. FLM을 사용하는 경우 프리리스트, 프리리스트 그룹 등의 속성을 이용해 관리자가 수동(Manual)으로 관리 가능한 반면, ASSM을 사용하는 경우에는 더 이상 이러한 속성을 지원하지 않으며 관리자가 세그먼트 공간 관리 기법의 행동 양식을 변경할 수 없다. 이런 의미에서, 즉 수동으로 관리가 불가능하다는 의미에서 자동화된 세그먼트 공간 관리 기법으로 이름이 붙여졌다.

ASSM에서 사용하는 프리 블록 관리 기법은 대단히 복잡하고 지능적이다. 여기서는 중요한 개념과 성능의 관점에서 유리한 점들만을 집중적으로 살펴볼 것이다.

프리 블록의 의미

ASSM에서의 프리 블록의 의미는 FLM에서와 동일하다. 즉, INSERT 작업을 위해 사용 가능한 여유 블록을 의미한다. 하지만 프리 블록을 판단하는 기준은 새롭게 정의되었으며 더욱 세련된 방식의 블록 공간 관리 개념을 사용한다. ASSM에서도 특정 블록이 프리(Free) 상태인지 풀(Full) 상태인지의 구분은 여전히 존재하지만, 블록이 얼마나 여유가 있는지를 판단하는 새로운 기준을 사용한다. 이것을 여유도 상태(Freeness Status. FS)라고 부른다. 여유도 상태는 다음과 같은 네 가지 종류의 값을 지닌다.

  • FS1: 블록의 여유 공간이 0~25% 인 상태
  • FS2: 블록의 여유 공간이 25~50% 인 상태
  • FS3: 블록의 여유 공간이 50~75% 인 상태
  • FS4: 블록의 여유 공간이 75~100% 인 상태

블록의 프리 여부는 여전히 PCTFREE 속성 값을 이용해 판단하지만, 프리 블록이 얼마나 여유가 있는지를 다시 FS 값을 이용해 판단하게 된다. 이렇게 함으로써 오라클이 최대한 블록을 효율적으로 사용할 수 있도록 보장한다. 오라클은 PCTFREE 속성과 FS 값을 이용해 다음과 같은 기준으로 프리 블록을 여부를 결정한다.

  • 익스텐트가 추가로 할당되는 과정에서 생긴 HWM 아래에 존재하는 한번도 사용되지 않은 블록. FLM에서와 동일하다.
  • INSERT 문에 의해 로우(Row)가 추가되었지만, 아직 PCTFREE 속성에 의해 지정된 영역을 다 사용하지 않은 블록. FLM의 경우와 동일하다.
  • PCTFREE 속성에 의해 지정된 영역을 다 소모한 후, 다시 DELETE나 UPDATE에 의해 사용률이 낮아지되, FS 상태가 변하는 경우. FLM에서는 풀 블록이 프리 블록으로 바뀌는 기준으로 PCTUSED 속성이 사용되는 반면, ASSM에서는 더 이상 PCTUSED 속성이 사용되지 않는다. ASSM에서는 풀 상태의 블록에서 데이터가 지워지면서 FS의 상태가 바뀌어야만, 가령 FS1(0~25% 여유) 상태에서 FS2(25~50% 여유) 상태로 바뀌는 경우에 다시 프리 블록으로 인식된다. FLM에 비해 블록의 사용 정도를 구분하는 기준이 더욱 세련되어졌음을 알 수 있다.

아래 그림을 보면 ASSM에서 PCTFREE 속성과 FS 값이 어떻게 블록의 프리 여부를 결정하는지 알 수 있다. 그림:Pctfree속성.jpg

오라클이 제공하는 DBMS_SPACE 패키지를 이용하면 세그먼트 공간에 대한 다양한 정보를 얻을 수 있다. 특히DBMS_SPACE.SPACE_USAGE 프로시저를 이용하면 특정 세그먼트의 FS1, FS2, FS3, FS4, Full, Unformatted 상태의 블록 수와 크기(바이트)에 대한 정보를 얻을 수 있다. 다음은 DBMS_SPACE.SPACE_USAGE 프로시저를 사용하는 간단한 예제이다.

DECLARE
	v_unformatted_blocks	NUMBER;
	v_unformatted_bytes		NUMBER;
	v_fs1_blocks			NUMBER;
	v_fs1_bytes			NUMBER;
	v_fs2_blocks			NUMBER;
	v_fs2_bytes			NUMBER;
	v_fs3_blocks			NUMBER;
	v_fs3_bytes			NUMBER;
	v_fs4_blocks			NUMBER;
	v_fs4_bytes			NUMBER;
	v_full_blocks			NUMBER;
	v_full_bytes			NUMBER;
BEGIN
DBMS_SPACE.SPACE_USAGE(
   segment_owner      =>'OWI',
   segment_name       =>'BIG1',
   segment_type       =>'TABLE',
   unformatted_blocks	=> v_unformatted_blocks,
   unformatted_bytes	=> v_unformatted_bytes,
   fs1_blocks		=> v_fs1_blocks,
   fs1_bytes		=> v_fs1_bytes,
   fs2_blocks		=> v_fs2_blocks,
   fs2_bytes		=> v_fs2_bytes,
   fs3_blocks		=> v_fs3_blocks,
   fs3_bytes		=> v_fs3_bytes,
   fs4_blocks		=> v_fs4_blocks,
   fs4_bytes		=> v_fs4_bytes,
   full_blocks		=> v_full_blocks,
   full_bytes		=> v_full_bytes
);

DBMS_OUTPUT.PUT_LINE('v_unformatted_blocks = ' || v_unformatted_blocks);
DBMS_OUTPUT.PUT_LINE('v_unformatted_bytes = ' || v_unformatted_bytes);
DBMS_OUTPUT.PUT_LINE('v_fs1_blocks = ' || v_fs1_blocks);
DBMS_OUTPUT.PUT_LINE('v_fs1_bytes = ' || v_fs1_bytes);
DBMS_OUTPUT.PUT_LINE('v_fs2_blocks = ' || v_fs2_blocks);
DBMS_OUTPUT.PUT_LINE('v_fs2_bytes = ' || v_fs2_bytes);
DBMS_OUTPUT.PUT_LINE('v_fs3_blocks = ' || v_fs3_blocks);
DBMS_OUTPUT.PUT_LINE('v_fs3_bytes = ' || v_fs3_bytes);
DBMS_OUTPUT.PUT_LINE('v_fs4_blocks = ' || v_fs4_blocks);
DBMS_OUTPUT.PUT_LINE('v_fs4_bytes = ' || v_fs4_bytes);
DBMS_OUTPUT.PUT_LINE('v_full_blocks = ' || v_full_blocks);
DBMS_OUTPUT.PUT_LINE('v_full_bytes = ' || v_full_bytes);

END;
/
v_unformatted_blocks = 0
v_unformatted_bytes = 0
v_fs1_blocks = 0
v_fs1_bytes = 0
v_fs2_blocks = 0
v_fs2_bytes = 0
v_fs3_blocks = 0
v_fs3_bytes = 0
v_fs4_blocks = 0
v_fs4_bytes = 0
v_full_blocks = 237
v_full_bytes = 1941504

3단계 비트맵 블록

ASSM을 사용하는 세그먼트는 프리 블록들의 목록을 관리하기 위해 더 이상 프리리스트(Freelist)를 사용하지 않는다. 대신 3단계의 비트맵 블록(Bitmap Block)을 이용해서 보다 효율적이고 입체적으로 세그먼트 공간을 관리한다. 아래 그림에 3 단계의 비트맵 블록과 프리 블록과의 관계가 표현되어 있다. 그림:비트맵블록.jpg

L1BMB(Level 1 Bitmap Block)는 각 블록의 여유도 상태(Freeness Status. FS)를 관리하는 역할을 한다. 하나의 L1BMB가 세그먼트의 크기에 따라 16 ~ 1,024 개의 블록의 상태를 관리한다. 아래 예제는 필자의 시스템에서 하나의 L1BMB를 덤프로 내려 받은 것으로, 총 64 개의 블록의 상태를 관리하는 것을 확인할 수 있다. 블록의 상태는 “Unformatted, FULL, 0-25% free, 25-50% free, 50-75% free, 75-100% free” 중 하나의 값이 된다.

 --------------------------------------------------------
 DBA Ranges :   L1BMB가 관리하는 블록의 목록들
 --------------------------------------------------------
  0x01804109  Length: 64     Offset: 0      
  0:Metadata   1:Metadata   2:Metadata   3:Metadata
  4:Metadata   5:Metadata   6:Metadata   7:Metadata
  8:Metadata   9:Metadata   10:Metadata   11:Metadata
  12:Metadata   13:Metadata   14:Metadata   15:Metadata
  16:Metadata   17:Metadata   18:Metadata   19:Metadata
  20:Metadata   21:Metadata   22:FULL   23:FULL
  24:FULL   25:FULL   26:FULL   27:FULL
  28:FULL   29:FULL   30:FULL   31:FULL
  32:FULL   33:FULL   34:FULL   35:FULL
  36:FULL   37:FULL   38:FULL   39:FULL
  40:FULL   41:FULL   42:FULL   43:FULL
  44:FULL   45:FULL   46:FULL   47:FULL
  48:FULL   49:FULL   50:FULL   51:FULL
  52:FULL   53:FULL   54:FULL   55:FULL
  56:FULL   57:FULL   58:FULL   59:FULL
  60:FULL   61:FULL   62:FULL   63:FULL
 --------------------------------------------------------

L2BMB(Level 2 Bitmap Block)는 L1BMB의 목록을 관리한다. 하나의 L2BMB가 여러 개의 L1BMB를 관리하게 된다. 아래 예제는 하나의 L2BMB를 덤프로 내려 받은 것으로, 총 20개의 L1BMB를 관리하고 있는 것을 알 수 있다.

 --------------------------------------------------------
  0x01804109  Free: 1 Inst: 1 
  0x0180410a  Free: 1 Inst: 1 
  0x0180410b  Free: 1 Inst: 1 
  0x0180410c  Free: 1 Inst: 1 
  0x0180410d  Free: 5 Inst: 1 
  0x0180410e  Free: 5 Inst: 1 
  0x0180410f  Free: 5 Inst: 1 
  0x01804110  Free: 5 Inst: 1 
  0x01804111  Free: 5 Inst: 1 
  0x01804112  Free: 5 Inst: 1 
  0x01804113  Free: 5 Inst: 1 
  0x01804114  Free: 5 Inst: 1 
  0x01804115  Free: 5 Inst: 1 
  0x01804116  Free: 5 Inst: 1 
  0x01804117  Free: 5 Inst: 1 
  0x01804118  Free: 5 Inst: 1 
  0x01804119  Free: 5 Inst: 1 
  0x0180411a  Free: 5 Inst: 1 
  0x0180411b  Free: 5 Inst: 1 
  0x0180411c  Free: 5 Inst: 1 
 --------------------------------------------------------

L2BMB는 L1BMB에 대해서 다음과 같은 정보를 관리한다.

  • L1BMB DBA
  • L1BMB가 관리하는 블록들의 최대 여유도(Maximum Freeness). 1=Full, 2=FS1, 3=FS2, 4=FS3, 5=FS4를 의미한다. 오라클은 공간의 효율성을 높이기 위해 가장 여유도가 높은 L1BMB를 먼저 사용한다.
  • 소유 인스턴스(Owning Instance). RAC 환경에서 해당 L1BMB를 소유한 인스턴스의 번호를 의미한다. RAC 환경에서는 이 정보를 이용해서 인스턴스 간에 프리 블록을 둘러싼 경합을 최소화한다. ASSM에서 프리리스트 그룹(Freelist Group) 기능이 사라진 이유가 여기에 있다.

L3BMB는 L2BMB의 목록을 관리한다. 하나의 L3BMB가 여러 개의 L2BMB를 관리할 수 있다. L3BMB는 대부분의 경우 별도의 물리적인 블록으로 존재하지 않고, 세그먼트 헤더 블록 내부에 존재한다. 세그먼트의 크기가 매우 커서 하나의 L3BMB로 관리가 불가능할 때에만 별도의 L3BMB가 물리적으로 분리된다. 아래 예제는 L3BMB(세그먼트 헤더)를 덤프로 내려 받은 것으로, L2BMB에 대한 부가적인 정보(L2 Hint for inserts)와 L2BMB의 DBA 값을 확인할 수 있다.

Segment Type: 1 nl2: 1      blksz: 8192   fbsz: 0          
  L2 Array start offset:  0x00001434                       
  First Level 3 BMB:  0x00000000                           
  L2 Hint for inserts:  0x0180411d                         
  Last Level 1 BMB:  0x0180411c                            
  Last Level II BMB:  0x0180411d                           
  Last Level III BMB:  0x00000000                          
…                                                         
                                                           
   Second Level Bitmap block DBAs                          
   --------------------------------------------------------
   DBA 1:   0x0180411d

오라클은 L3BMB, L2BMB, L1BMB를 순차적으로 탐색하면서 새로운 데이터를 추가할 가장 이상적인 프리 블록을 찾는다. ASSM에서 사용하는 3단계의 비트맵 블록 구조는 이전 프리리스트 구조에 비해 상당히 유연하고 자동화되어 있어서, FLM을 사용할 때와 같은 외부적인 튜닝이나 설정이 불필요하다. 성능 또한 잘 튜닝된 FLM을 사용할 때와 거의 비슷하다. 다만 대량의 DML이 발생하는 경우, 비트맵 블록을 관리하기 위한 오버헤드가 발생하기 때문에 이로 인한 약간의 성능 저하가 발생할 수 있다.

RAC와 ASSM

ASSM은 RAC를 위한 세그먼트 관리 기법이라고 해도 무방할 정도로 RAC 환경에 최적화되어 있다. ASSM이 제공하는 다음과 같은 특징을 보면 오라클이 RAC에 최적화된 세그먼트 관리 기법을 제공하기 위해 노력한 흔적을 찾아볼 수 있다.

  • L1BMB의 리소스 친화도: L1BMB가 처음 생성될 때 RAC 시스템 중 어느 인스턴스가 이 블록에 대한 소유권을 가질 지를 결정한다. 즉 L1BMB는 리소스 친화도를 갖도록 설계되었다. 서로 다른 인스턴스는 서로 다른 L1BMB를 사용하게 되므로 프리 블록을 둘러싼 경합이 최소화된다. FLM에서의 프리리스트 그룹 기능과 거의 비슷한 효과를 제공하는 셈이다. 프리리스트 그룹 기능은 수동적인 설정이 필요하고 클러스터의 멤버들이 변하는 상황에 대해 적응력이 매우 낮다는 점을 고려하면 ASSM의 우수성을 깨닫게 될 것이다.
  • L1BMB 훔치기: L1BMB는 리소스 친화도를 가지고 있기 때문에 특정 인스턴스에 속하게 된다. 이것은 일반적으로 바람직한 기능이지만 만일 특정 인스턴스가 특정 L1BMB를 계속해서 점유한다면 데이터 비대칭(Data Skew) 현상이 생길 수 있다. 데이터 비대칭이란 특정 인스턴스가 세그먼트의 특정 부분을 독점함으로써 다른 인스턴스가 해당 부분을 전혀 사용할 수 없게 되는 현상을 의미한다. FLM에서 프리리스트 그룹을 사용하는 경우 데이터 비대칭 현상이 발생할 수 있다는 것은 앞서 언급한 바 있다. ASSM에서는 데이터 비대칭 현상을 원천적으로 피하기 위해 L1BMB 훔치기(BMB Stealing) 기능을 제공한다. L1BMB 훔치기 기능은 말 그대로 다른 인스턴스가 소유한 L1BMB를 훔쳐서 사용하는 기능을 말한다. 오라클은 세그먼트 공간 사용 요구 정도가 지나치게 많다고 판단되면 다른 인스턴스가 소유한 L1BMB를 훔쳐와서 사용한다. 특정 인스턴스가 클러스터에서 빠져 나간 경우에도 해당 인스턴스가 소유한 L1BMB는 다른 인스턴스에 의해 훔쳐진다. 엄격한 리소스 친화도가 아닌 필요에 따라 소유 인스턴스가 변할 수 있다는 의미에서 이러한 기능을 약 친화도(Soft Affinity)라고 부르기도 한다. 즉 L1BMB는 약 친화도를 가지며 이를 통해 FLM에서 문제가 되었던 데이터 왜곡 현상을 원천적으로 방지한다.

다시 한번 정리하면, ASSM의 공간 관리 기법은 RAC 시스템에서 각 인스턴스간의 프리 블록 경합을 최소화할 수 있도록 설계되었으며, 이로 인해 INSERT 작업 시 노드 간의 경합이 최소화된다.

ASSM과 비트맵 블록 경합

ASSM이 FLM에 비해 여러 가지 뛰어난 장점을 제공하지만 대량의 DML이 발생하는 상황에서는 오버헤드가 발생할 수 있다. 아이러니한 것은 FLM의 단점을 제거하기 위해 설계된 3단계의 비트맵 블록으로 인해 이러한 오버헤드가 발생한다는 것이다.

L1BMB는 개별 블록들의 여유도 상태(Freeness Status)를, L2BMB는 개별 L1BMB들의 상태를, L3BMB는 개별 L2BMB들의 상태를 관리한다. 만일 개별 블록들의 여유도 상태가 변경되면 L1BMB에 대한 변경(Update) 작업이 발생한다. L1BMB의 상태가 변경되면 L2BMB에 대한 변경 작업이 발생한다. 마지막으로 L2BMB의 상태가 변경되면 L3BMB에 대한 변경 작업이 발생한다. 대량 DML이 발생하는 경우 비트맵 블록의 변경이 빈번하게 발생할 수 있다. 특히 L1BMB와 L2BMB에 대한 변경 작업이 많이 발생하게 되며 이로 인해 약간의 성능 저하가 발생할 수 있다. 만일 동시에 여러 세션이 동일 세그먼트에 대해 INSERT를 수행한다면, L1BMB와 L2BMB를 동시에 변경하게 되고 이로 인해 buffer busy waits이벤트에 대해 대기시간이 증가한다. 이 경우 블록 클래스(Class#)는 8번(L1BMB) 또는 9번(L2BMB)로 관찰된다.

ASSM에서 비트맵 블록의 경합에 의해 약간의 성능 저하 현상이 발생하지만, 이것은 ASSM이 제공하는 다른 많은 장점들에 의해 상쇄된다. 특히 RAC 환경에서는 어떤 이유를 막론하고 FLM이 아닌 ASSM을 사용하는 것이 좋다. 만일 굳이 FLM을 사용한다면 프리리스트 그룹을 노드 수와 동일하게 부여해야 한다. 하지만 FLM을 사용하는 경우 데이터 비대칭 현상을 피할 수 없다는 사실에 주의해야 한다.

ASSM과 FB 락 경합

ASSM을 사용하는 세그먼트에 대한 대량 DML 작업 시에 FB 락 경합이 발생하는 경우가 종종 발생한다. FB 락은 Formatting Block Lock을 의미한다. FB 락의 의미를 이해하려면 ASSM을 사용하는 세그먼트에는 두 개의 HWM(High Water Mark)가 존재한다는 사실을 이해할 필요가 있다. ASSM에서는 하나의 익스텐트가 여러 개의 L1BMB에 의해 나누어 관리될 수 있다. 만일 특정 익스텐트의 앞 부분과 뒤 부분은 L1BMB에 의해 관리되고, 중간 부분은 아직 L1BMB가 할당되지 않았다면 중간에 구멍(Hole)이 생기게 된다. 즉 세그먼트의 중간 중간에 전혀 사용되지 않는 비 포맷 상태의 블록들이 다수 존재할 수 있다. 이러한 현상은 FLM에서는 찾아볼 수 없는 ASSM만의 고유한 공간 관리 기법에 의해 초래된다. 이러한 이유로 오라클은 Low HWM과 High HWM 이라는 두 개의 HWM을 관리한다. Low HWM 이하의 블록들은 모두 포맷 상태의 블록으로 “현재 사용 중”인 블록들이다. High HWM 이상의 블록들은 모두 비 포맷 상태의 블록이며, “아직 사용되지 않은” 블록들이다. Low HWM과 High HWM 사이에는 아직 사용되지 않은 비 포맷 상태의 블록들이 존재할 수 있다. 이러한 비 포맷 상태의 블록들은 추후에 INSERT 작업에 의해 사용되는 시점에 포맷되며 이 과정에서 FB 락을 획득한다. 많은 수의 프로세스들이 동시에 같은 영역의 비 포맷 블록을 사용하고자 하는 경우 FB 락 경합이 발생하게 되고, enq: FB – contention 이벤트에 대한 대기로 관찰된다. FB 락 경합은 ASSM 환경에서는 피할 수 없는 현상이며, 성능에 주는 영향도 미미하다. 아래 샘플은 세그먼트 헤더 블록에 대한 덤프 파일로 세그먼트 헤더 블록 내에서 Low HWM과 High HWM을 같이 관리하는 것을 확인할 수 있다.

Start dump data blocks tsn: 7 file#: 6 minblk 16670 maxblk 16673
buffer tsn: 7 rdba: 0x0180411e (6/16670)
scn: 0x0000.00472a3d seq: 0x01 flg: 0x04 tail: 0x2a3d2301
frmt: 0x02 chkval: 0x7e6e type: 0x23=PAGETABLE SEGMENT HEADER
Hex dump of block: st=0, typ_found=1
…
Extent Control Header
  -----------------------------------------------------------------
  Extent Header:: spare1: 0      spare2: 0      #extents: 1      #blocks: 1280  
                  last map  0x00000000  #maps: 0      offset: 2716  
      Highwater::  0x0180420c  ext#: 0      blk#: 259    ext size: 1280  
  #blocks in seg. hdr's freelists: 0     
  #blocks below: 259   
  mapblk  0x00000000  offset: 0     
                   Unlocked
  --------------------------------------------------------
  Low HighWater Mark : 
      Highwater::  0x0180420c  ext#: 0      blk#: 259    ext size: 1280  
  #blocks in seg. hdr's freelists: 0     
  #blocks below: 259   
  mapblk  0x00000000  offset: 0     
  Level 1 BMB for High HWM block: 0x0180410d
Level 1 BMB for Low HWM block: 0x0180410d
…

아래 스크립트는 ASSM을 사용하는 세그먼트에 대해 동시 다발적인 대량 INSERT 작업을 수행하는 경우에 어떤 종류의 경합들이 발생하는지를 테스트한 결과로, HW 락 경합, 버퍼 락 경합, FB 락 경합 등이 주로 목격되는 것을 알 수 있다.

-- ASSM을 사용하는 테이블 스페이스 생성
create tablespace fb_tbs1
datafile '/home/oracle/oradata/10gr2/ORA102/fb_tbs1.dbf' size 10M
autoextend on
segment space management auto
extent management local uniform size 5M

-- 테이블 생성(1로우 = 1블록)
create table fb_table1(id number,
 name1 char(2000) default 'a',
 name2 char(2000) default 'a',
 name3 char(2000) default 'a',
 name4 char(1500) default 'a')
tablespace fb_tbs1;

-- FB_TABLE1에 500건을 추가하는 프로시저
create or replace procedure fb_insert
is
begin
	for idx in 1 .. 500 loop
		insert into fb_table1(id) values(idx);
	end loop;
end;

-- 동시에 20개의 세션에서 SQL*Trace를 활성화시킨 상태에서 FB_INSERT 프로시저를 수행하고, trcsess 툴을 이용해서 20개의 세션이 남긴 트레이스 파일을 병합한 뒤 tkprof 툴을 이용해 대기회수와 시간 정보 추출한 결과는 다음과 같다.

INSERT INTO FB_TABLE1(ID)
VALUES
(:B1 )

Call	count	cpu	elapsed	disk	query	current		rows
-----	------	-----	------	-----	----	------		----
Parse	21	0.00	0.02	0	0	0		0
Execute	10500	17.22	605.20	0	20176	100188		10500
Fetch		0	0.00	0.00	0	0	0		0
------ 	------	-----	------	-----	----	------		----
Total	10521	17.22	605.22	0	20176	100188		10500

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 55     (recursive depth: 1)

Elapsed times include waiting on following events:
Wait Event				Total		Total		Time
						Waited		Timeout		Waited
------------------------------	-------		-------		--------
  enq: HW ? contention			1364		2.82		348.29
  latch: In memory undo latch		4		0.00		0.00
  enq: FB ? contention			1692		0.17		9.71
  buffer busy waits			9276		0.29		100.79
  latch: redo copy			522		0.14		1.68
  latch free				108		0.01		0.41
  latch: cache buffers chains		359		0.22		2.06
  enq: TX ? contention			270		2.63		4.07
  log buffer space			656		0.29		55.26
  latch: redo allocation		20		0.00		0.02
  latch: enqueue hash chains		74		0.09		1.61
  wait list latch free			9		0.02		0.21
  cursor: pin S				9797		0.00		0.00
  latch: undo global data		11		0.02		0.09
  …

HW 락 경합은 세그먼트 확장에 따라 HWM을 이동하는 과정에서 발생하면 enq: HW – contention 이벤트 대기로 관찰된다. FB 락 경합은 Low HWM과 High HWM 사이의 구멍(Hole)에 해당하는 블록을 포맷하는 과정에서 발생하며 enq: FB – contention 이벤트에 대기로 관찰된다. 버퍼 락 경합은 블록들의 여유도 상태가 변경됨에 따라 비트맵 블록의 정보를 변경하는 과정에서 발생하며 블록 클래스 값이 8 또는 9인 buffer busy waits 이벤트에 대한 대기로 관찰된다. FLM을 사용하는 경우에는 FREELISTS 속성이나 FREELIST GROUPS 속성, 혹은 _BUMP_HIGHWATER_MARK_COUNT 파라미터 값을 변경함으로써 이러한 경합들에 대한 튜닝이 가능했지만, ASSM에서는 이러한 외부적인 튜닝을 할 수 없다. 따라서 문제가 발생한 원인을 바탕으로 어플리케이션의 작동 방식을 개선하는 방식의 튜닝 접근법을 따라야 한다.



추가 설명  : 


출처 : http://shocky00.springnote.com/pages/990322


1. freelist를 통한 free block관리

8i까지에서, segment의 free block들은 항상 freelist를 통해 관리된다. PCTUSED아래로 채워진 block들이 freelist로 연결되어 있어서, insert가 필요하면 이 freelist를 segment header에서부터 뒤지면서 블럭내의 빈 공간에 insert를 하게 되는것이다.
같은 table에 대해서 insert 트랜잭션이 동시에 많은 경우 table storage의 freelists 값을 증가시켜야 하고, OPS의 경우 node갯수를 고려하여 freelist groups을 지정해야 하는 등 freelist와 관련하여 DB Admin이 고려하여야 할 tuning point가 존재하여 왔다.
또한 이 freelist내의 free block에 대한 정보가 segment header내에 전체 정보를 가지고 있거나 dictionary table에 정보를 가지고 있는 것이 아니고, linked list 형태로 free block이 다음 free block을 지정하는 형태라 쉽게 freelist에 대한 정보를 확인하는 것도 불가능하다.
이렇게 전체 freelist에 대한 정보를 쉽게 확인하지 못하는 이유로, table에 대한 reorganization을 결정하는 기준을 정하기도 쉽지 않았고, db내에서 space 활용도 최적이 되지 못한다.
이러한 space관리에 대한 문제점을 극복하기 위해 9i에서 제시된 것이 ASSM (automatic space segment management)이다.

2. Automatic Space Segment Management

9i에서 제시된 이 ASSM방식은 segment에 할당된 space를 bitmap으로 관리한다.
ASSM 방식을 이용하려면 반드시 locally managed tablespace여야 하며, 다음과 같이 'segment space management auto'를 지정하면된다.

 

  1. SQL>CREATE TABLESPACE test_tbs
    DATAFILE '/oracle/data/data01.dbf' SIZE 50M
    EXTENT MANAGEMENT LOCAL
    SEGMENT SPACE MANAGEMENT AUTO;

 

auto가 아닌 manual로 지정되게 되면 이전과 같이 freelist방식을 사용하게 되며, DBA_TABLESPACES view의 SEGMENT_SPACE_MANAGEMENT column을 통해 AUTO인지 MANUAL방식인지 확인 가능하다.
이렇게 생성된 tablespace내에 table이나 index를 생성하게 되면 segment header 외에 추가적인 BMB (BitMap Blocks)라는 것이 생기게 된다. 이 BMB에는 할당된 block들의 space정보를 4 bit를 이용하여 다음 6가지 상태를 나타내는 bitmap 정보를 가진다.

 

(1) 75% 이상의 free space를 가지는 block
(2) 50% 이상 75% 미만의 free space를 가지는 block
(3) 25% 이상 50% 미만의 free space를 가지는 block
(4) 25% 미만의 free space를 가지는 block
(5) 꽉 찬 block
(6) 한번도 사용하지 않아 format 되지 않은 block

 

이렇게 ASSM 방법을 이용하여 space를 관리하게 되면 free block에 대해서 좀 더 상세한 정보를 바탕으로 space utilization도 높아지고, freelist를 타고 다음다음 block을 access하는대신 BMB를 참고로 적당한 block들을 선택하기 때문에 space에 관한 성능도 좋아진다.
또한 ASSM의 경우 해당 tablespace에 생성된 segment들은 freelists, freelist groups, pctused등은 지정하여도 무시되는데, 이것은 space관리 작업을 단순화시킬뿐 아니라, 특히 freelist groups 지정이 성능에 영향을 미쳤던 RAC에서 큰 도움이 된다. 그외에도 BMB를 동시에 다른 트랜잭션이 다른 part를 access 하는 것 또한 성능에 도움을 준다. 오라클은 internal benchmark 자료에 따르면 RAC에서 최적화된 freelist 관련 parameter를 지정한 상태에서 3백만건 데이타 insert 작업에 대해서, ASSM과 일반 manual 방법을 비교해 본 결과 35 % 정도 ASSM사용이 성능향상에 도움이 되었다.
이때 주의할 점은, locally managed tablespace와 이 ASSM 방식간에 혼동을 일으키는 경우가 종종 있다. 이것은 locally managed tablespace와 ASSM이 둘 다 bitmap방식을 통해 space를 관리한다는 측면때문 일 것으로 보인다.
ASSM방식을 사용하려면 미리 이야기한대로 locally managed tablespace에서만 사용가능한데, 기본적으로 locally managed tablespace는 dba_free_space에서 확인되는 할당되지 않은 space에 대한 관리이고, ASSM은 일단 segment내에 할당된 extent안에서 block내의 free space에 관한 것이다.
locally managed tablespace에 관한 좀 더 자세한 사항은 <Bulletin No: 11860>이나 <Bulletin No: 18261>를 참조하도록 한다.

3. space정보 확인 방법

ASSM방식을 사용하지 않고 freelist방법을 사용하는 경우 segment에 할당된 extent 내에 free block을 확인하는 방법은, table analyze후 DBA_TABLES의 EMPTY_BLOCK를 확인하거나 DBMS_SPACE.UNUSED_SPACE procedure를 이용하는 방법이 존재한다. 그리고 이 두 가지는 같은 정보를 보여준다.
ASSM으로 관리되는 table이나 index에 대해서는 DBA_TABLES의 EMPTY_BLOCKS와 DBMS_SPACE.UNUSED_SPACE가 다른 값을 나타낼 뿐 아니라, ASSM의 정확한 정보를 모두 나타내지 못하므로 새로이 소개된 DBMS_SPACE.SPACE_USAGE procedure를 이용하면 된다.
이 세가지 방법이 나타내는 각 정보에 대해서 자세히 살펴본다.
먼저 DBMS_SPACE.SPACE_USAGE의 사용방법이다. 아래에 예로 SCOTT user의 TEST table에 대해서 예로 들었다. TEST table은 ASSM 방식으로 관리되는 table이다.

 

  1. SQL> declare
    2 v_unformatted_blocks number;
    3 v_unformatted_bytes number;
    4 v_fs1_blocks number;
    5 v_fs1_bytes number;
    6 v_fs2_blocks number;
    7 v_fs2_bytes number;
    8 v_fs3_blocks number;
    9 v_fs3_bytes number;
    10 v_fs4_blocks number;
    11 v_fs4_bytes number;
    12 v_full_blocks number;
    13 v_full_bytes number;
    14 begin
    15 dbms_space.space_usage ('SCOTT', 'TEST', 'TABLE', v_unformatted_blocks,
    16 v_unformatted_bytes, v_fs1_blocks, v_fs1_bytes, v_fs2_blocks,
    17 v_fs2_bytes, v_fs3_blocks, v_fs3_bytes, v_fs4_blocks,
    18 v_fs4_bytes, v_full_blocks, v_full_bytes);
    19 dbms_output.put_line('Unformatted Blocks = '||v_unformatted_blocks);
    20 dbms_output.put_line('FS1 Blocks = '||v_fs1_blocks);
    21 dbms_output.put_line('FS2 Blocks = '||v_fs2_blocks);
    22 dbms_output.put_line('FS3 Blocks = '||v_fs3_blocks);
    23 dbms_output.put_line('FS4 Blocks = '||v_fs4_blocks);
    24 dbms_output.put_line('Full Blocks = '||v_full_blocks);
    25 end;
    26 /
  2. Unformatted Blocks = 0
  3. FS1 Blocks = 0
    FS2 Blocks = 0
    FS3 Blocks = 0
    FS4 Blocks = 1
    Full Blocks = 9
  4. PL/SQL procedure successfully completed.

 

이때, FS1 ~ FS4가 의미하는것은 다음과 같다.

 

FS1 : 0-25%의 free space를 가진 block
FS2 : 25-50%의 free space를 가진 block
FS3 : 50-75%의 free space를 가진 block
FS4 : 75-100%의 free space를 가진 block

 

이예에서 SCOTT user의 TEST table에 data를 insert시키고 analyze후 조회하니 다음과 같은 결과가 나왔다. (단, DBMS_SPACE.SPACE_USAGE사용을 위해서는 analyze를 수행할 필요가 없다.)
아래의 예에서 보면 첫번째로 DBA_TABLES의 EMPTY_BLOCKS와 DBMS_SPACE.UNUSED_SPACE에서 보여주는 값이 서로 다르다. 그리고 정확한 정보를 주는 DBMS_SPACE_SPACE_USAGE에서는 75%이상 비어있는 block이 한개, full인 block이 9개로 나타난다. 그럼 전체 13개의 block중 3개의 block은 어떠한 block 일까? 이 3개의 BLOCK을 DBA_TALES에서는 EMPTY_BLOCK로 분류하였다.
이 세개의 block는 바로 space를 bitmap으로 관리하는 BMB 들이다.

DBA_TABLES DBMS_SPACE.UNUSED_SPACE DBMS_SPACE.SPACE_USAGE
--------------------- ---------------------------- ------------------------
BLOCKS | EMPTY_BLOCKS TOTAL_BLOCKS | UNUSED_BLOCKS FS4 Blocks | Full Blocks
--------------------- ---------------------------- ------------------------

10 3 13 0 1 9
 

Reference Documents
-------------------

<Note:180608.1> Automatic Space Segment Management in RAC Environments

<Note:149516.1> BMB versus Freelist Segment: DBMS_SPACE.UNUSED_SPACE

and DBA_TABLES.EMPTY_BLOCKS


DBMS_RANDOM

DBMS_RANDOM

The DBMS_RANDOM package provides a built-in random number generator. DBMS_RANDOM is not intended for cryptography.

This chapter contains the following topics:


Using DBMS_RANDOM


Security Model

This package should be installed as SYS. By default, the package is initialized with the current user name, current time down to the second, and the current session.


Operational Notes

  • DBMS_RANDOM.RANDOM produces integers in [-2^^31, 2^^31).

  • DBMS_RANDOM.VALUE produces numbers in [0,1) with 38 digits of precision.

DBMS_RANDOM can be explicitly initialized, but does not need to be initialized before calling the random number generator. It will automatically initialize with the date, userid, and process id if no explicit initialization is performed.

If this package is seeded twice with the same seed, then accessed in the same way, it will produce the same results in both cases.

In some cases, such as when testing, you may want the sequence of random numbers to be the same on every run. In that case, you seed the generator with a constant value by calling one of the overloads ofDBMS_RANDOM.SEED. To produce different output for every run, simply to omit the call to "Seed" and the system will choose a suitable seed for you.


Summary of DBMS_RANDOM Subprograms

Table 75-1 DBMS_RANDOM Package Subprograms

SubprogramDescription

INITIALIZE Procedure

Initializes the package with a seed value

NORMAL Function

Returns random numbers in a normal distribution

RANDOM Procedure

Generates a random number

SEED Procedures

Resets the seed

STRING Function

Gets a random string

TERMINATE Procedure

Terminates package

VALUE Functions

This function gets a random number, greater than or equal to 0 and less than 1, with 38 digits to the right of the decimal (38-digit precision), while the overloaded function gets a random Oracle number x, where x is greater than or equal to low and less than high


Note:

The INITIALIZE ProcedureRANDOM Procedure and the TERMINATE Procedure are all obsolete and, while currently supported, are included in this release for legacy reasons only.

INITIALIZE Procedure

This procedure initializes the generator (but see Usage Notes).

Syntax

DBMS_RANDOM.INITIALIZE (
   val  IN  BINARY_INTEGER);

Pragmas

PRAGMA restrict_references (initialize, WNDS)

Parameters

Table 75-2 INITIALIZE Procedure Parameters

ParameterDescription

val

The seed number used to generate a random number.


Usage Notes

This procedure is obsolete as it simply calls the SEED Procedures.


NORMAL Function

This function returns random numbers in a standard normal distribution.

Syntax

DBMS_RANDOM.NORMAL
  RETURN NUMBER;

Pragmas

PRAGMA restrict_references (normal, WNDS)

Return Values

Table 75-3 NORMAL Procedure Parameters

ParameterDescription

number

Returns a random number.



RANDOM Procedure

This procedure generates a random number (but see Usage Notes).

Syntax

DBMS_RANDOM.RANDOM
   RETURN binary_integer;

Pragmas

PRAGMA restrict_references (random, WNDS)

Return Values

Table 75-4 RANDOM Procedure Parameters

ParameterDescription

binary_integer

Returns a random integer greater or equal to -power(2,31) and less than power(2,31).


Usage Notes

This procedure is obsolete and, although it is currently supported, it should not be used.


SEED Procedures

This procedure resets the seed.

Syntax

DBMS_RANDOM.SEED (
   seed  IN  BINARY_INTEGER);

DBMS_RANDOM.SEED (
   seed  IN  VARCHAR2);

Pragmas

PRAGMA restrict_references (seed, WNDS);

Parameters

Table 75-5 SEED Procedure Parameters

ParameterDescription

seed

Seed number or string used to generate a random number.


Usage Notes

The seed can be a string up to length 2000.


STRING Function

This function gets a random string.

Syntax

DBMS_RANDOM.STRING
   opt  IN  CHAR,
   len  IN  NUMBER)
  RETURN VARCHAR2;

Pragmas

PRAGMA restrict_references (string, WNDS)

Parameters

Table 75-6 STRING Function Parameters

ParameterDescription

opt

Specifies what the returning string looks like:

  • 'u', 'U' - returning string in uppercase alpha characters

  • 'l', 'L' - returning string in lowercase alpha characters

  • 'a', 'A' - returning string in mixed case alpha characters

  • 'x', 'X' - returning string in uppercase alpha-numeric characters

  • 'p', 'P' - returning string in any printable characters.

Otherwise the returning string is in uppercase alpha characters.

len

The length of the returning string.


Return Values

Table 75-7 STRING Function Return Values

ParameterDescription

VARCHAR2

Returns a VARCHAR2.



TERMINATE Procedure

When you are finished with the package, call the TERMINATE procedure (but see Usage Notes)

Syntax

DBMS_RANDOM.TERMINATE

Usage Notes

This procedure performs no function and, although it is currently supported, it is obsolete and should not be used.


VALUE Functions

The basic function gets a random number, greater than or equal to 0 and less than 1, with 38 digits to the right of the decimal (38-digit precision). Alternatively, you can get a random Oracle number x, where x is greater than or equal to low and less than high.

Syntax

DBMS_RANDOM.VALUE
  RETURN NUMBER;

DBMS_RANDOM.VALUE(
  low  IN  NUMBER,
  high IN  NUMBER)
RETURN NUMBER;

Parameters

Table 75-8 VALUE Function Parameters

ParameterDescription

low

The lowest number in a range from which to generate a random number. The number generated may be equal to low.

high

The highest number below which to generate a random number. The number generated will be less than high.


Return Values

Table 75-9 VALUE Function Return Values

ParameterDescription

NUMBER

Returns an Oracle Number.



2009년 3월 17일 화요일

ORA-00600

PURPOSE

이 문서는 오라클 shadow 프로세스에게 할당되었던 메모리를 free 하는 과정에서 만날 수 있는 
memory leak problem 에 대해서 소개하고자 한다. 메모리 leak dump 는 주로 session 이 logoff하는
과정에서 만날 수 있는 것으로서 user process에게 할당되었다가 사용된 heap을 
ORACLE 서버가 free할 때 발생하는 것이다.

Problem Description

alert log 및 트레이스 화일에는 이와 같은 에러가 남게 된다. 

ORA-00600: internal error code, arguments: 729560space leak], [], [], [


Explanation

Heap dump의 내용과 어떤 경우에 이런 에러가 발생하는지에 대해 좀 더 자세히 알아보기로 한다.

Oracle이 process를 종료하면, 점유하였던 메모리와 release되는 memory를 비교하여 
size가 정확히 일치하지 않으면 ora-600729 에러를 발생시키고, shutdown 시에 SGA 등을 
모두 release하고 일부 메모리 영역이 여전히 남아있으면 ora-600730을 report하게 된다.

User가 oracle에 접속하면 user process가 생성이 되고, 각 user process마다 heap이 할당된다.
모든 프로세스들은 자신의 memory heap을 갖게 된다.
Heap을 구성하는 각 extent는 연속적인 chunk로 이루어져 있는데, 이러한 chunk들은 각각 
다음과 같은 type들을 가질 수 있다. 

1. FREE : free chunk는 heap manager가 다시 할당할 수 있는 비어있는 부분.
Free 영역은 할당된 chunk가 해제가 된 상태로 이것은 인접한 free chunk들과 
merge한 다음에 Free list에 등록된다.

2. FREEABLE : freeable은 flush를 시켜서 다시 merge를 할 수 있는 후보.
SYS.X$KSMSP를 조회하면 freeable space에 대한 조각을 확인할 수 있다.

3. RECREATABLE : memory chunk가 heap에 할당될 때, chunk의 내용이 재생성가능한 것으로 
명시할 수 있다. 이러한 option으로 생성되면, 이 chunk는 사용 중이지 않을 때 명시적으로 
'unpinned'될 수 있다. 사용자가 heap으로부터 space를 요청할 때 공간이 없으면, 
이러한 unpinned 혹은 recreatable chunk를 해제하고 사용할 수 있다. 
Unpinned chunk는 LRU list에 존재하게 되어 무엇을 가장 먼저 비게 할 것인지 결정할 수 
있으며, 이러한 방법은 row cache나 library cache에 이용된다.

4. PERMANENT : 이 영역은 일단 할당되면 해제되지 않는 메모리 부분인데, 각 heap descriptor는
이러한 permanent chunk 부분을 가리키는 pointer를 갖고 있다.
이 permanent chunk는 두 부분으로 나누어지는데, 앞 부분은 이미 permanent로 할당된
메모리 부분이고, 뒷 부분은 아직 할당되지 않은 reserved area 이다.
Chunk의 header에는 어느 부분부터가 reserved area의 시작인지 나타내는 pointer가 있다.
실제 permanent 메모리는 프로세스가 종료하여 heap 자체가 해제되면 그 영역도 해제가 된다. 

5. FREEABLE WITH MARK

Permanent 메모리 영역을 해제할 때 완전히 free 시키지 않고, Freeable with MARK 상태로 
두었다가 다시 restore 하기도 하는데, 이 chunk type은 oracle 10g 에서는 널리 알려져
있지는 않다. 

Extent의 할당과 해제 

연속된 chunk들의 집합을 extent라고 한다.
즉, heap에 포함되어 있는 extent들의 집합 안에서 요청한 크기의 조각을 할당받게 된다. 
만약 이 때 발견하지 못하면, heap manager는 새로운 extent를 요청하여 이것을 heap에 추가한다.

각 extent는 오직 한 종류의 chunk type만 갖는 것이 아니고, 다양한 type의 chunk를 가질 수 있다.
프로세스가 memory chunk를 요청할 때 heap manager는 필요한 size 만큼의 공간을 할당해 준다.
프로세스가 종료하면 프로세스에게 할당되었던 모든 메모리는 자동으로 release가 된다.
프로세스 종료(logoff) 시 RECREATABLE chunk와 FREEABLE chunk는 FREELIST 로 등록되었다고 
판단을 하고, 메모리가 release될 때 아직 남아 있는 할당된 heap들은 free가 되는데, 
ORACLE server 입장에서는 일반적으로 heap이 free가 될 때 PERMANENT chunk로 할당된 chunk와 
freelist 상에 있는 FREE chunk들을 free시킬 대상으로 삼는다. 

프로세스가 FREEABLE 혹은 RECREATABLE type 으로 남아 있는 chunk를 발견한 경우는 
해당 프로세스가 memory deallcation 을 제대로 수행하지 않았다는 것을 의미하게 된다. 
이러한 상황을 space leak 이라 부른다. 
오라클은 일반적으로 SGA heap, UGA heap, Large pool heap, 그리고, PGA heap에 대해 
space leak을 check한다. Space leak error는 BACKGROUND_DUMP_DEST 또는 
USER_DUMP_DEST 내에 trace file 안에 남게 된다.

Symptoms

Space leak problem은 일반적으로 trace information과 heap dump를 capture하여 떨어뜨리게 된다.
OS와 ORACLE process header 정보 다음에 trace file 내에 이와 같은 정보를 볼 수 있다. 

*** 2006-10-03 18:43:11.598
*** SESSION ID:(34.50354) 2006-10-03 18:43:11.597
******** ERROR: UGA memory leak detected 560 ********

바로 위 라인을 보면 이 memory leak 은 UGA 영역에서 발생한 것이고, leak이 일어난 size는 
560 bytes임을 알 수 있다. 

Heap은 연속된 chunk의 집합으로 이루어지고, heap이란 heap descriptor와 extent라고 불리는 
메모리 조각의 집합으로 구성되며, 각 extent는 연속된 메모리 조각으로 정의가 된다.

****************************************************** 
HEAP DUMP heap name="session heap" desc=0xaef81d0 
extent sz=0xffb8 alt=32767 het=32767 rec=0 flg=3 opc=3 
parent=0xaeb63e0 owner=0x7a4b7078 nex=(nil) xsz=0xffb8 

위 dump에서 heap name은 SESSION HEAP 이고, Heap descriptor는 0xaef81d0 임을 알 수 있다. 

아래의 extent 상세 정보를 보면 release되지 않은 chunk는 4085a350임을 나타낸다.
이것은 recreatable chunk이고, 그 size는 560 bytes이다. 이 수치는 729560space leak
에러에 나타난 bytes와 수치가 정확히 일치한다.

EXTENT 0 addr=0x407cf048 
Chunk 407cf050 sz= 65456 free " " 
EXTENT 1 addr=0x408a0048 
Chunk 408a0050 sz= 65456 free " " 
EXTENT 2 addr=0x40890048 
Chunk 40890050 sz= 65456 free " " 
EXTENT 3 addr=0x40850048 
Chunk 40850050 sz= 41728 free " " 
Chunk 4085a350 sz= 560 recreate "bind var heap " latch=(nil) 
EXTENT 4 addr=0x407df048 
Chunk 407df050 sz= 65456 free " " 
EXTENT 5 addr=0x40f91048 
Chunk 40f91050 sz= 65456 free " " 
EXTENT 6 addr=0x40880048 
Chunk 40880050 sz= 65456 free " " 
EXTENT 7 addr=0x40870048 
Chunk 40870050 sz= 65456 free " " 

메모리 leak issue를 분석할 때 FREEABLE과 RECREATABLE type의 chunk를 식별해야 한다.
그 수치는 에러의 arguments에 나타난 leak 현상을 보이는 memory bytes의 sum 과 일치해야 한다.

원인

ora-600729 error는 memory leak 현상으로 발생하는 오라클 에러라 할 수 있다.
그러나, 이러한 현상은 정상적인 경우도 발생할 수 있으며, 지속적으로 반복적으로 발생하여 
memory free 영역이 계속 줄어드는 현상으로 나타나지 않는 한, 특별히 문제 시 되지 않으며, 
일정 사이즈 이하의 leak은 message로 나타나게 하지 않도록 조치할 수 있다.


Workaround

아래의 예는 2M 이하의 leak 발생 시 에러가 나지 않게 하는 것으로, 만약 100k정도나 1~2M 이하의 
leak 문제가 보인다면 발생하지 않게 이와 같이 셋팅할 수 있다.

event="10262 trace name context forever, level 2000000"


Solution Description

Space leak 문제를 어떻게 다룰지에 대해 자세한 사항은 다음의 문서에서 안내하고 있다.

Step 1. alert log를 review하고, trace file 정보를 분석해야 한다.
alert log 에는 이와 같은 에러가 남았을 것이다.

Sat Dec 02 21:52:17 2006 
Errors in file d:\oracle\admin\testdb\udump\testdb_ora_5928.trc: 
ORA-00600: internal error code, arguments: 729152space leak], [], [], [ 

a. argument 729는 space leak 문제 발생 시 대표적인 에러 code이다.
b. argument 
152는 에러에 보고된 leak된 bytes의 수치이다.
c. argument 
space leak 는 항상 space leak 으로 나타난다

Step 2. 연관된 트레이스 화일 분석 

일반적인 trace information은 다음과 같다.

*** 2006-12-13 02:01:13.859 
*** SESSION ID:(54.11635) 2006-12-13 02:01:13.859 
******** ERROR: UGA memory leak detected 152 ******** 
****************************************************** 

위 트레이스는 UGA로부터 leak된 메모리라는 것과 memory leak의 size가 152 bytes 라는 것을 
의미한다.


Step 3. leak이 session logoff로부터 발생했다는 것을 확인한다.

Call Stack Trace 부분을 보면 opilof 가 stack 상에 보인다면 이것은 session logoff 시 
발생했음을 알 수 있다.
실제 발생한 call stack trace의 예는 다음과 같다.

Call Stack Trace
==========
calling call entry argument values in hex 
location type point (? means dubious value) 
========= ======== ============ ============== 
ksedmp()+184 ? ksedst() 800000010000B938 ? 
ksfdmp()+32 ? ksedmp() 800003FFBFFF6418 ? 
kgeriv()+152 ? ksfdmp() 20000000B168 ? 
kgesiv()+132 ? kgeriv() 40000000000002D9 ? 
ksesic2()+124 ? kgesiv() 000000000 ? 
ksmuhe()+1040 ? ksesic2() 000000000 ? 
ksmugf()+400 ? ksmuhe() 000000000 ? 
ksuxds()+2692 ? ksmugf() 800003FFBFFF4020 ? 
ksudel()+104 ? ksuxds() 8000000100131B38 ? 
opilof()+876 ? ksudel() 800003FFBFFF5808 ? 
opiodr()+2416 ? opilof() 0650AB9D8 ? 
ttcpip()+1320 ? opiodr() 8000000100004790 ? 
opitsk()+1260 ? ttcpip() 000000100 ? 
opiino()+1484 ? opitsk() 8000000100138268 ? 
opiodr()+2416 ? opiino() 000001560 ? 
opidrv()+752 ? opiodr() 800003FFBFFF0870 ? 
sou2o()+40 ? opidrv() 000000000 ? 
main()+228 ? sou2o() 000000000 ?

Step 4. Dedicated Server인지, MTS 환경인지 확인한다.

1) 만약 Dedicated server 환경이라면 이 error의 impact은 process가 종료할 때 끝날 것이다.
사실 에러의 영향도는 거의 없고, 데이타베이스에 실질적인 문제는 없을 것이다.
2) MTS(Multi Threaded Server) 환경이거나 XA transaction process manager/monitor를 사용하는 환경이라면
leaked memory가 SGA 영역에 있을 것이다. 이 때에는 ora-4030, ora-4031 과 같은 다른 에러가 발생했는지 
alert log를 보아야 한다. 

Step 5. Leak problem을 무시할 수 있는가 ?

1) 이 물음에 대해서는 다른 에러는 없는지 우선 살펴보아야 한다. 만약 동시에 발생한 다른 에러가 전혀 없다면
이 에러는 무시해도 되고, 다시 재현하기가 쉽지 않은 case일 것이다. 무엇보다도 90,000 bytes 보다 작은
size의 leak은 중요하지 않다. 이에 대한 solution은 event 10262 를 셋팅하는 것이다. 

2) 만약 leak이 SGA 내에 발생했는가?
ALERT log를 살펴보고, shared pool 이나 OS memory 와 관련한 추가적인 문제는 없는지 ORA-4030 또는 
ORA-4031 발생 여부를 확인해야 한다.

3) 메모리 leak 현상이 특정 작업을 수행 시에 재현이 되는가?
만약 특정 작업 시에 재현이 된다면, leak 현상이 known bug인지 확인을 위해 
추가적인 investigation이 필요한 경우이다. 
다음 문서를 보면 known bug 와 fix된 version들을 확인할 수 있다.

Note 31056.1 ORA-600 729 UGA Space Leak for a list of known bugs and fixes

Step 6. Event 10262 셋팅하기

만일 leak된 bytes 수치가 무시해도 될 만한 수치라는 것이 확인이 되면, 이 에러를 
무시하기 위해 event를 걸 수 있다.

이 event를 셋팅을 하게 되면 90,000 bytes보다 작은 크기의 leak에 대해서는 alert log에 나타나지 않게 할 수 있다.

a. 다음과 같이 initSID.ora file에 event를 셋팅한다. 

event="10262 trace name context forever, level 90000"

b. 데이타베이스를 restart해야 event가 효력을 미친다.

참고 

만일 level 을 1로 하게 되면, space leak checking이 disable된다. 1로 하게 되면 매우 
큰 사이즈의 memory leak을 놓칠 수 있으므로, 권장하지 않는다. 
Level 을 1보다 큰 수로 하게 되면 event 안에 명시된 숫자보다 작은 memory leak에 
대해서는 무시하도록 하는 역할을 한다.

Reference documents

<Note:403584.1> Understanding and Diagnosing ORA-600 729 Space Leak Errors


출처 : http://kr.forums.oracle.com/forums/thread.jspa?messageID=3254377&#3254377 

Shell Script 명령어

출처 : http://kldp.org/KoreanDoc/Shell_Programming-KLDP 

쉘 프로그래밍 강좌

참고서적 :  초보자용 리눅스 프로그래밍
            ( 대림출판사, 한동훈,이만용역, NEIL MATTHEW, RICHARD STONES 저 )

※ 넷츠고 리눅스 동호회 7월 제 5회 정기 공개강좌 자료
글쓴이 : 위경섭 ( powerhack@netsgo.com )

1. 변수
  . 쉘변수는 처음 사용될때 만들어진다. 즉 미리 선언할 필요가 없다.
  . 쉘변수는 유닉스 명령과 마찬가지로 대소문자에 구별이 있다.
  . 쉘변수는 기본적으로 데이터를 문자열로 저장한다. 수치를 대입해도 실제 수치
    가 아닌 문자열이 저장된다. 계산이 필요할 경우는 자동으로 수치로 변환하여 
    계산후 다시 문자열로저장된다.
  . 쉘변수의 값을 사용할 때는 변수명앞에 "$" 를 붙여서 사용한다.
  . 쉘변수에 값을 대입할때는 "$"를 사용하지 않는다.
  . 쉘변수는 타입이 없다. 즉 아무 값이나 다 넣을 수 있다.
	
1.1 환경변수
  쉘을 기동하고나면 기본적으로 셋팅되어있는 변수들이다. 유닉스/리눅스에는 많은
  환경변수들이 있고 필요한경우 이 변수들을 마치 일반변수처럼 값을 얻어오거나 셋
  팅할 수 있다. 여기서는 쉘과 직접적인 관련이 있는것만 설명한다. 
		
  $0 - 실행된 쉘 스크립트 이름
  $# - 스크립트에 넘겨진 인자의 갯수
  $$ - 쉘 스크립트의 프로세스 ID
		
1.2 인자 변수
  쉘스크립트에 인자를 넘겨줄때 그 인자들에 대한 정보를 가지고 있는 변수들.
		
  $1~ $nnn  : 넘겨진 인자들
  $*        : 스크립트에 전달된 인자들을 모아놓은 문자열. 하나의 변수에 저장되며
              IFS 환경변수의 첫번째 문자로 구분된다.
  $@        : $*과 같다. 다만 구분자가 IFS변수의 영향을 받지 않는다.
	
1.3 일반변수
  일반변수에 특별한 제약은 없다. 단 대소문자 구분만 정확하게 해주면 된다.

  예제 )

  #!/bin/sh
  echo "This Script Executable File : $0"
  echo "Argument Count : $#"
  echo "Process ID : $$"
  echo "Argument List \$* : $*"
  echo "Argument List \$@ : $@"
  echo "Argument 1 : $1"
  echo "Argument 2 : $2"
  echo "Argument 3 : $3"
  echo "Argument 4 : $4"	
  	
  실행 )
  $chmod 755 test1
  $./test1 a1 a2 a3 a4
  This Script Executable File : ./test1
  Argument Count : 4
  Process ID : 905
  Argument List $* : a1 a2 a3 a4
  Argument List $@ : a1 a2 a3 a4
  Argument 1 : a1
  Argument 2 : a2
  Argument 3 : a3
  Argument 4 : a4
	
1.4 연산
  변수의 산술연산은 생각하는것 처럼 쉽지않다. 위에서 언급했듯이 변수에는 모든것
  이 문자열로 저장되기 때문에 연산이 불가능하다. 연산을 위해서는 좀 복잡한 절차
  를 거쳐야 한다.

  변수 = $((산술식))
	
  이것이 가장 단순한 연산 규칙이다. 산술식내에는 변수( $1, $a 와 같은 ) 도 들어
  갈 수 있다. 산술식 내에 숫자가 아닌 문자열, 또는 문자열이 담겨있는 변수가 들어
  가면 그것들은 계산에서 제외된다.
  (정확히 말하면 0 으로 간주되어 연산이 이루어 지지 않는다.)
	
1.5 매개변수 확장
  매개변수 확장이란 변수의 값을 문자열등으로 대체하는 것을 말한다. 단순한 대체뿐
  아니라 변수내의 문자열을 조작하여 원하는 문자열만을 추출할 수도 있다.

  형식
  ${parm:-default}	: parm이 존재하지 않으면 default로 대체된다.
  ${#parm}          : parm의 길이를 참조한다.(가져온다)
  ${parm%word}      : 끝에서부터 word와 일치하는 parm의 최소부분(첫번째 일치) 을
                      제거하고 나머지를 반환한다.
  ${parm%%word}     : 끝에서부터 word와 일치하는 parm의 최대부분(마지막 일치) 을
                      제거하고 나머지를 반환한다.
  ${parm#word}      : 처음부터 word와 맞는 parm의 최소부분(첫번째 일치)을 제거하
                      고 나머지 부분을 반환한다.
  ${parm##word}     : 처음부터 word와 맞는 parm의 최대부분(마지막 일치)을 제거하
                      고 나머지를 반환한다.
		
  * word에는 와일드 카드를 사용할 수 있다.
		
  예를 보자.
  		
  1 #!/bin/sh
  2
  3 p="/usr/X11R6/bin/startx"
  4
  5 unset p
  6 a=${p:-"Variable p Not found"}
  7 echo $a
  8
  9 p="/usr/X11R6/bin/startx"
  10 a=${p:-"Variable parm Not found"}
  11 echo $a
  12
  13 a=${#p}
  14 echo $a
  15
  16 a=${p%/*}
  17 echo $a
  18
  19 a=${p%%/*}
  20 echo $a
  21
  22 a=${p#*/}
  23 echo $a
  24
  25 a=${p##*/}
  26 echo $a
  27                    
		
  위 스크립트의 결과는 다음과 같다.
  ---------------------------------
  Variable p Not found
  /usr/X11R6/bin/startx
  21
  /usr/X11R6/bin
  
  usr/X11R6/bin/startx
  startx              
  ----------------------------------
   6행 : 변수 p 가 제거 되었으므로 "Variable p Not found" 가 a에 들어간다.
  10행 : 변수 p 가 있으므로 그대로 a에 들어간다.
  13행 : a에는 변수 p의 길이가 들어간다.
  16행 : p 에서 가장 오른쪽의 "/"부터 끝까지 지우고 나머지를 a에 넣는다.
  19행 : p 에서 가장 왼쪽의 "/" 부터 끝까지 지우고 나머지를 a에 넣는다.
         (아무것도 없다)
  22행 : p 의 처음부터 가장왼쪽의 "/" 까지 지우고 나머지를 a에 넣는다.
  25행 : p 의 처음부터 가장 오른쪽의 "/"까지 지우고 나머지를 a에 넣는다.
		
2. 조건 판단
  쉘 스크립트에서 조건판단은 if 와 test 명령을 혼합하여 사용한다.
  일반적인 예는 다음과 같다.
	
  if test -f test1
  then
  	...
  fi
  
  -f 는 주어진 인자가 일반파일 일때 참이된다.
  
  
  test 명령은  [] 로 대체될 수 있다.
  
  if [ -f test1 ]
  then
  	...
  fi
  
  -----------------------------
  
  if [ -f test1 ]; then
  	...
  fi
	
  2.1 test 명령
  
  test 명령의 조건은 다음과 같이 세 부류로 나누어진다.
	
  문자열비교
    [ string ]             : string이 빈 문자열이 아니라면 참
    [ string1 = string2 ]  : 두 문자열이 같다면 참
    [ string1 != string2 ] : 두 문자열이 다르면 참
    [ -n string ]          : 문자열이 null(빈 문자열) 이 아니라면 참
    [ -z string ]          : 문자열이 null(빈 문자열) 이라면 참
		
  산술비교
    [ expr1 -eq expr2 ]	: 두 표현식 값이 같다면 참 ( EQual )
    [ expr1 -ne expr2 ]	: 두 표현식 갑이 같지 않다면 참 ( Not Equal )
    [ expr1 -gt expr2 ]	: expr1 > expr2 이면 참 ( Greater Then )
    [ expr1 -ge expr2 ]	: expr1 >= expr2 이면 참 ( Greater Equal )
    [ expr1 -lt expr2 ]	: expr1 < expr2 이면 참 ( Less Then )
    [ expr1 -le expr2 ]	: expr1 <= expr2 이면 참 ( Less Equal )
    [ ! expr ]          : expr 이 참이면 거짓, 거짓이면 참
    [ expr1 -a expr2 ]  : expr1 AND expr2 의 결과 ( 둘다 참이면 참 )
    [ expr1 -o expr2 ]  : expr1 OR expr2 의 결과 ( 둘중 하나만 참이면 참 )
		
  파일조건

    [ -b FILE ]           : FILE 이 블럭 디바이스 이면 참
    [ -c FILE ]           : FILE 이 문자 디바이스 이면 참.
    [ -d FILE ]           : FILE 이 디렉토리이면 참
    [ -e FILE ]           : FILE 이 존재하면 참
    [ -f FILE ]           : FILE 이 존재하고 정규파일이면 참
    [ -g FILE ]           : FILE 이 set-group-id 파일이면 참
    [ -h FILE ]           : FILE 이 심볼릭 링크이면 참
    [ -L FILE ]           : FILE 이 심볼릭 링크이면 참
    [ -k FILE ]           : FILE 이 Sticky bit 가 셋팅되어 있으면 참
    [ -p FILE ]           : True if file is a named pipe.
    [ -r FILE ]           : 현재 사용자가 읽을 수 있는 파일이면 참
    [ -s FILE ]           : 파일이 비어있지 않으면 참
    [ -S FILE ]           : 소켓 디바이스이면 참
    [ -t FD   ]           : FD 가 열려진 터미널이면 참
    [ -u FILE ]           : FILE 이 set-user-id 파일이면 참
    [ -w FILE ]           : 현재 사용자가 쓸 수 있는 파일(writable file) 이면 참
    [ -x FILE ]           : 현재사용자가 실행할 수 있는 파일(Executable file) 이면 참
    [ -O FILE ]           : FILE 의 소유자가 현재 사용자이면 참
    [ -G FILE ]           : FILE 의 그룹이 현재 사용자의 그룹과 같으면 참
    [ FILE1 -nt FILE2 ]   : FILE1이 FILE2 보다 새로운 파일이면 ( 최근파일이면 ) 참
    [ FILE1 -ot FILE2 ]   : FILE1이 FILE2 보다 오래된 파일이면 참
    [ FILE1 -ef FILE2 ]   : FILE1 이 FILE2의 하드링크 파일이면 참
		
  2.2 if 구문
    if 문은 조건을 판단하여 주어진 문장을 수행한다.
		
    1. 형식 1  ( 단일 if 문 )
    형식 :
      if [ 조건 ]
      then
        문장1
        문장2
      fi
				
    2. 형식 2  ( if~else 문 )
    형식 :
      if [ 조건 ]
      then
        문장3
        문장4
      fi
				
    3. 형식 3  ( if~elif 문 )
    형식 :
      if [ 조건 ]
      then
        문장1
        문장2
      elif
        문장3
        문장4
      else
        문장5
        문장6
      fi
				
  2.3 case 구문
  ※ 패턴에는 * 문자, 즉 와일드카드를 사용할 수 있다.
  형식 :
      case 변수 in
        패턴 [ | 패턴 ] ... ) 문장 ;;
        패턴 [ | 패턴 ] ... ) 문장 ;;
        ....
        * ) 문장 ;;
      easc
		
  2.4 목록
    여려명령을 실행할때 앞의 명령의 결과에 의해서 다음행동이 결정되어야 할 경우
    가 있다. 이런경우에 AND나 OR조건을 사용해서 한번에 처리할 수 있다. 이것은 쉘
    스크립트 뿐 아니라 명령행에서도 사용 가능하다. 물론 if 문을 이용해서 반환값
    을 검사하여 처리할 수 있지만 문장이 길어지고 복잡해진다.
		
    AND 목록
	
        statment1 && statment2 && statmentN && .....

        위의 명령들은 각 명령이 거짓이 될 때 까지 명령을 수행해 나간다. 수행도중
        결과가 거짓이 되면 그이후의 명령은 수행되지 않는다.
	
    OR  목록
	
        statment1 || statment2 || statmentN || .....
	
        위의 명령들은 각 명령이 거짓이 나오는 동안 계속된다. 즉 참이 나오면 실행
        을 멈춘다.
	
    AND와 OR목록은 혼용이 가능하다.
	
        [ 조건 ] && 문장1 || 문장2

        위의 예는 조건이 참이면 문장1을 수행하고 거짓이면 문장2를 수행한다.

        또한 위의 문장1이나 문장2에서 여러개의 문장을 수행 하고 싶을 때는 {}를
        사용하면 된다.

        [조건] && {
                    문장1
                    문장2
                    문장3
                          } || {
                                 문장4
                                 문장5
                                 문장6
                               }
	
3. 제어문
  3.1 for
  for 문은 지정된 범위안에서 루프를 수행한다. 범위는 어떤 집합도 가능하다.
  형식 :
         for 변수 in 값1, 값2, ...
         do
             문장
         done

  매 루프를 돌때마다 변수의 값은 in 이후의 값으로 대체된다.
  예)
      for str in "test1", "test2", "test3", "test4"
      do
         echo @str
      done

  출력 )

      test1
      test2
      test3
      test4
		
  값에는 와일드 카드 확장을 사용할 수 있다.

    for file in $(ls -a | grep "^.")
    do
      echo "$file is Hidden File"
    done

  위 예의 출력 결과는 현재디렉토리에서 처음이 "." 으로시작하는 파일(히든파일) 만
  을 출력한다.

  for file in $(ls chap[345].txt); do
      echo "--- $file ---" >> Books.txt
      cat $file >> Books.txt
  done

  위의예는 chap3.txt, chap4.txt, chap5.txt 파일을 Books.txt 라는 파일에 붙여 넣
  는다.

  다음의 예를 보고 결과를 예측해보자

  echo "\$* output"

  for fvar in $*
  do
    echo $fvar
  done

  echo "\$@ output"
  for fvar in $@
  do
    echo $fvar
  done
				
  3.2 while
    for 명령의 경우는 횟수를 지정해서 루프를 수행하는데는 문제가 있다.
    while 문은 실행횟수가 지정되지 않았을때 편리하다.
		
    형식 :
           while 조건문
           do
                문장
           done
			
    예제를 보자. 패스워드를 입력받고 맞는지 확인하는 프로그램이다.

    echo "Enter Password : "
    read password1

    echo "Retype Password : "
    read password2

    while [ "$password1" != "$password2" ]
    do
         echo "Password miss match Try again "

         echo "Retype Password : "
         read password2
    done

    echo "OK Password Match complete"
		


    어떻게 동작하는가 ?
				
  3.3 until
    until은 while문과 동일한 효과를 내지만 조건이 반대이다. 즉, while문은 조건이
    참 일동안 루프를 수행하지만 until은 조건이 거짓일 동안 루프를 수행한다.
    
    형식 :
      until 조건문
      do
      	문장
      done
    
    다음 예를 보자. 이 예는 지정한 유저가 로그인 하면 알려준다.
    
    #!/bin/sh
    
    until who | grep "$1" > /dev/null
    do
        sleep 10
    done
    
    echo "User $1 just logged in ^_^"
		
	3.4 select
    select 문은 원하는 리스트를 출력하고 그중 선택된것을 돌려주는 구문이다. 주의
    할점은 select의 루프내에서는 자동적으로 루프를 벗어날 수 없다. 반드시 break
    문을 사용해서 루프를 벗어나야 한다.
		
    예) 간단한 퀴즈 ^_^

      #!/bin/sh
      
      echo "다음중 스크립트언어 프로그래밍에 속하는 것은 ?"
      select var in "쉘 프로그래밍" "C 프로그래밍" "자바 프로그래밍" "Exit"
      do
          if [ "$var" = "쉘 프로그래밍" ]
          then
                  echo "정답입니다."
                  exit 0
          elif [ "$var" = "Exit" ]
          then
                  echo "종료합니다."
                  exit 1
          else
                  echo "$var 을 선택하셨습니다. 오답입니다."
                  echo "다음중 스크립트언어 프로그래밍에 속하는 것은 ?"
          fi
      done

4. 함수
  쉘 스크립트 내부에 또는 다른 스크립트파일에 함수를 정의해 놓고 사용할 수 있다.
  함수를 사용하면 코드를 최적화 할 수 있고, 코딩이 간결해지며,재사용이 가능하다.
  그러나 다른 스크립트 파일을 호출해서 함수를 실행할 경우, 가능은 하지만 스크립
  트의 실행시간이 길어지고, 함수의 결과를 전달하는 것이 까다롭기 때문에 가급적이
  면 외부파일의 함수는 안쓰는 것이 좋다.
	
  형식 :
    정의 - 
    함수명 ()
    {
    	문장
    	return 값
    }
    
    사용
    
    함수명 인자1, 인자2, ...
    
    
    함수는 독립적으로 $#, $*, $0 등의 인자변수를 사용한다. 즉 함수내의 $#과 본체
    의 $#은 다를 수 있다는 것이다.
    
    다음의 예를 보자
    
    #!/bin/sh
		
    func()
    {
      echo ------ this is func --------
      echo "This Script Executable File : $0"
      echo "Argument Count : $#"
      echo "Process ID : $$"
      echo "Argument List \$* : $*"
      echo "Argument List \$@ : $@"
      echo "Argument 1 : $1"
      echo "Argument 2 : $2"
      echo "Argument 3 : $3"
    }
		
    echo ------ this is main --------
    echo "This Script Executable File : $0"
    echo "Argument Count : $#"
    echo "Process ID : $$"
    echo "Argument List \$* : $*"
    echo "Argument List \$@ : $@"
    echo "Argument 1 : $1"
    echo "Argument 2 : $2"
    echo "Argument 3 : $3"
    echo "Argument 4 : $4"
    func aa bb cc 


    본체와 함수에서 동일한 변수를 보여주지만 값은 틀린다는것을 알 수 있다.
    
    함수에서 값을 반환하기 - 함수에서 반환값은 반드시 정수값만을 반환할 수 있다.
    이 값을 if등으로 조건을 판단해서 사용할 수 있다. 반환값중 0은 참으로 나머지 
    숫자는 거짓으로 판별된다.
		
5. 명령어
  쉘에서 쓸 수 있는 명령어는 두가지로 나누어진다. 명프롬프트 상에서 실행 시킬 수
  있는 외부 명령어와 쉘 내부 명령이다. 내부명령은 보통 쉘 내부나 쉘 구문상에서
  쓰인다. 외부명령은 쉘에 관계없이 사용이 가능하다.
	
  break
    제어문이나 조건문의 루프를 빠져나갈때 사용한다.
    예)
    	while [ $a -eq 10 ]
    	do
    		if [ $a -eq 5 ]; then
    			break
    		fi
    	done
	
  :명령
    의미없는 명령. 논리값 true를 대신해 쓰기도 한다.
		
  continue
    제어문이나 조건문의 처음으로 돌아가서 다시수행한다.
    예)
    	while [ $a -eq 10 ]
    	do
    	    if [ $a -eq 5 ]; then
               continue
            fi
    	done
		
  . 명령
    . 명령을 사용하면 현재 쉘에서 명령을 실행시킨다 그러므로 실행된 명령의 결과
    를 본 프로그램에서 사용할 수 있다.
    
    예를 들면 A 라는 스크립트에서 B라는 스크립트를 그냥 실행할 경우 B에서의 변화
    (환경변수 등)는 A에게 아무런 영향도 미치지 않는다. 그러나 . 명령을 사용해서 
    실행하면 B에서의 변화가 A에도 영향을 미친다.
		
  echo
    문장을 출력한다. 자동으로 개행문자가 삽입된다. ( 다음줄로 넘어간다 )
	
  eval
    인자의 실제 값을 구하는데 사용한다.
		
    foo=10
    x=foo
    y='$'$x
    echo $y
		
    이 예를 실행해 보면 $foo가 출력된다
		
    foo=10
    x=foo
    eval y='$'$x
    echo $y
		
    이 예에서는 $foo의 값 즉 10 이 출력된다. eval명령은 원하는 문자열들을 조합해
    서 변수를 액세스 할 수 있다.
	
  exec
    현재쉘을 다른 프로그램으로 대체한다.
		
    예 ) exec csh
	
  exit n
    현재 쉘을 종료한다. 종료시 n 값을 리턴한다.
	
  export
    해당 쉘에서 파생된 자식 프로세스에서 export한 환경변수는 본래 쉘에서 관리한
    다.
	
  expr
    표현식의 값을 구한다.    ( x=`expr 1 + 2` )
    요즘은 expr보다는 $((계산식)) 구문을 많이 사용한다.
	
  printf
    C 언어의 printf명령과 흡사하다.
    
    형식 :  printf "Format String" arg1 arg2 arg3 ...
		
  return
    쉘함수에서 값을 반환 할 때 쓰인다.
    0은 성공을 1~125까지는 쉘 에러코드를 나타낸다.
	
  set
    쉘 내부에서 매개 인자를 설정한다.
    set의 인자로 쓰인 문자열은 공백에 의해 $1 부터 차례대로 대입된다.
    
    예)
    
    #!/bin/sh
    echo $#
    set $(ls)
    echo $# 
    
    결과는 
    
    0
    22
    
    이다..( 22는 필자의 ls 결과의 갯수이다. ). 첫번째 0는 이 스크립트에 인수가
    없으므로 0이고 set $(ls) 에 의해서 인수의 갯수가 22개로 늘었다.
	
  shift
    쉘의 인자를 한자리씩 아래로( n -> 1 로 ) 이동시킨다.
    
    예)
    #!/bin/sh
    
    echo $1
    shift
    echo $1
    shift 5
    echo $1
    
    #./myscript 1 2 3 4 5 6 7 8 9 0
    1
    2
    7
	
	trap
    쉘의 실행도중 시그널을 처리하는 시그널 처리기를 만드는 역할을 한다.
    
    형식 : trap command signal
    
    쉘 스크립트는 위에서 아래로 실행되므로 보호하려는 부분 이전에 trap명령을 사
    용해야 한다. trap조건을 기본으로 사용하려면 명령에 - 를 넣으면 된다.
    신호를 무시하려면 '' 빈 문자열을 준다.
	
  unset
  	변수나 함수를 제거한다.

6. 명령실행
  외부명령의 실행 결과를 변수에 집어넣어 변수의 값으로 사용할 수 있다.
  
  형식 : x = $(명령)
  
  이렇게 변수에 결과를 넣은 후에는 이 변수를 일반문자열로 생각하고 원하는 가공을
  해서 결과를 얻어낼 수 있다.
  위에서 보았던 매개변수 확장이나 set명령을 이용해서 원하는 부분을 추출해 내면
  그만이다.

7. 쉘 스크립트 내부에서 명령에 입력 전달하기 ( Here Documents )
  이 기능은 쉘 내부에서 명령어에 입력을 전달하는 방법이다. 전달된 입력은 마치 키
  보드에서 눌려진 것 처럼 반응한다.
  
  형식 :  명령 << 종료문자열
  		입력값.....
  		종료문자열
  예제 ) 자동으로 메일을 보내는 스크립트
  
  #!/bin/sh
  
  mail $1 << myscript
  This is Header
  This is Body
  .
  
  myscript