레이블이 Storage인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Storage인 게시물을 표시합니다. 모든 게시물 표시

2009년 7월 31일 금요일

Understanding Oracle's Locally Managed Tablespaces

http://www.databasejournal.com/features/oracle/article.php/10893_2223631_1/Understanding-Oracles-Locally-Managed-Tablespaces.htm

Understanding Oracle's Locally Managed Tablespaces

By Amar Kumar Padhi

Locally Managed Tablespace (LMT) is one of the key features in Oracle database. These have been made available since Oracle 8i. It is worth using LMTs considering the benefits in doing so. I have put forward some scenarios that may be worth noting, for systems that are already using LMTs or planning to shift to LMTs.

Benefits of LMTs

Below are the key benefits offered by LMTs. Not all are achievable when migrating to LMTs.

  1. Dictionary contention is reduced.

    Extent management in DMTs is maintained and carried out at the data dictionary level. This requires exclusive locks on dictionary tables. Heavy data processing that results in extent allocation/deallocation may sometimes result in contentions in the dictionary.

    Extents are managed at the datafile level in LMTs. Dictionary tables are no longer used for storing extent allocation/deallocation information. The only information still maintained in the dictionary for LMTs is the tablespace quota for users.

  2. Space wastage removed.

    In DMTs, there is no implied mechanism to enforce uniform extent sizes. The extent sizes may vary depending on the storage clause provided at the object level or the tablespace level, resulting in space wastage and fragmentation.

    Oracle enforces the uniform extents allocation in the LMTs (when created with UNIFORM SIZE clause). Space wastage is removed, as this would result in all the same sized extents in the tablespace.

  3. No Rollback generated.

    In DMTs, all extent allocations and deallocations are recorded in the data dictionary. This generates undo information thus using vital resources and may compete with other processes.

    In LMTs, no rollback is generated for space allocation and deallocation activities.

  4. ST enqueue contention reduced.

    In DMTs, Space Transaction (ST) enqueue is acquired when there is a need for extent allocations in DMTs. It is also exclusively acquired by SMON process for coalescing free space in DMTs. Only one such enqueue exists per instance, and may sometimes result in contention and performance issues if heavy extent processing is being carried out. The following error is common in such scenario.

    ORA-01575: timeout warning for space management resource
    

    As ST enqueue is not used by LMTs it reduces the overall ST enqueue contention.

  5. Recursive space management operations removed.

    In DMTs, SMON process wakes up every 5 minutes for coalescing free space in DMTs. Optionally, the ALTER TABLESPACE <tablespace name> COALESCE command is also used to coalesce DMTs and reduce fragmentation.

    On the other hand, LMTs avoid recursive space management operations and automatically track adjacent free space, thus eliminating the need to coalesce free extents. This further reduces fragmentation.

  6. Fragmentation reduced.

    Fragmentation is reduced in LMTs but not completely eliminated. Since adjacent free spaces are automatically tracked, there is no need to do coalescing, as is required in the case of DMTs.

Management of Extents in LMTs

Oracle maintains a bitmap in each datafile to track used and free space availability in an LMT. The initial blocks in the datafiles are allocated as File Space Bitmap blocks to maintain the extent allocation information present in the datafile. Each bit stored in the bitmap corresponds to a block or a group of blocks. Whenever the extents are allocated or freed, oracle changes the bitmap values to reflect the new status. Such updates in the bitmap header do not generate any rollback information.

The number of blocks that a bit represents in a bitmap depends on the database block size and the uniform extent size allocated to the tablespace. For example, if the DB_BLOCK_SIZE parameter is set to 8K, and the tablespace is created with uniform extent sizing of 64K, then 1 bit will map to one 64K extent, i.e., 64K (extent size)/8K (block size) = 8 database blocks.

Allocation Types in LMTs

Allocation type plays a very important role in how the LMT is behaving. It specifies how the extent is being allocated by the system. There are three types of allocating extents in LMTs- USER, SYSTEM and UNIFORM.

  • USER- The LMT behaves as DMT, allocating extents as per the storage clause provided with the object or defaulted at tablespace level. The advantage is that allocation of extents is managed at the datafile level and such tablespaces will not compete for ST enqueue. The disadvantage is that such tablespaces are not subject to uniform extent allocation policy. DMTs that are converted to LMTs fall under this type.

  • SYSTEM- Oracle manages the space. The extents are auto allocated by the system based on an internal algorithm. Allocation of extents is managed at the datafile level and such tablespaces will not compete for ST enqueue. Such tablespaces would have extents of varying sizes and would result in fragmentation and some space being wasted. This is a good alternative if the extent sizes of the various objects to be placed in the tablespace cannot be determined.

  • UNIFORM- All extents are of fixed size in the system. The size is provided when creating the LMT. This type gives all the benefits offered by LMT and one should aim at achieving this.


    Storage parameters usage in LMT

    Storage parameters are used in DMTs to specify the object sizing. These parameters are not of much importance in UNIFORM type LMTs but play a role in deciding the initial allocation of space. Oracle considers the storage clause for the initial number of extents that should be allocated. For example, LMT is created with 32K extent size. The database block size is 8k.

    SQL> create table am05 (col1 number)
      2  storage (initial 100k next 100k minextents 1 maxextents unlimited pctincrease 0);
    
    SQL> select segment_name, segment_type, extent_id,  bytes, blocks
      2  from user_extents where segment_name = 'AM05';
    
    SEGMENT_NAME         SEGMENT_TYPE        EXTENT_ID      BYTES     BLOCKS
    -------------------- ------------------ ---------- ---------- ----------
    AM05                 TABLE                       0      32768          4
    AM05                 TABLE                       1      32768          4
    AM05                 TABLE                       2      32768          4
    AM05                 TABLE                       3      32768          4
    

    Oracle allocates four extents, the total size being 128K that is closer to the 100K provided for initial extent size. Please note that all the extents allocated have the uniform extent size of 32K. Only the number of extents to be allocated is decided based on the storage clause. See example below to clarify this.

    SQL> create table am06 (col1 number)
      2  storage(initial 200k next 100k minextents 2 maxextents unlimited pctincrease 0);
    
    SQL> select segment_name, segment_type, extent_id,  bytes, blocks
      2  from user_extents where segment_name = 'AM06';
    
    SEGMENT_NAME         SEGMENT_TYPE        EXTENT_ID      BYTES     BLOCKS
    -------------------- ------------------ ---------- ---------- ----------
    AM06                 TABLE                       0      32768          4
    AM06                 TABLE                       1      32768          4
    AM06                 TABLE                       2      32768          4
    AM06                 TABLE                       3      32768          4
    AM06                 TABLE                       4      32768          4
    AM06                 TABLE                       5      32768          4
    AM06                 TABLE                       6      32768          4
    AM06                 TABLE                       7      32768          4
    AM06                 TABLE                       8      32768          4
    AM06                 TABLE                       9      32768          4
    
    10 rows selected.
    
    SQL> select sum(bytes)/1024 from  user_extents where segment_name = 'AM06';
    
    SUM(BYTES)/1024
    ---------------
                320
    

    As per the storage clause, the table should be allocated 200K + 100K of space (since minextents is 2). Oracle rounds off on the higher side and allocates 10 extents of 32K, totaling 320K.

    Even pctincrease plays a role in uniform LMTs as the below example shows.

    SQL> create table am07  (col1 varchar2(200))
      2  storage(initial 16K next 16K minextents 5 maxextents unlimited pctincrease 50);
    
    Table created.
    
    SQL> select segment_name, segment_type, extent_id,  bytes, blocks
      2  from user_extents where segment_name = 'AM07';
    
    SEGMENT_NAME         SEGMENT_TYPE        EXTENT_ID      BYTES     BLOCKS
    -------------------- ------------------ ---------- ---------- ----------
    AM07                 TABLE                       0      32768          4
    AM07                 TABLE                       1      32768          4
    AM07                 TABLE                       2      32768          4
    AM07                 TABLE                       3      32768          4
    AM07                 TABLE                       4      32768          4
    
    SQL> select sum(bytes)/1024 from  user_extents where segment_name = 'AM07';
    
    SUM(BYTES)/1024
    ---------------
                160
    

    As per the storage clause the required initial size of the table should be 146K (16 + 16 + 24 + 36 + 54), Oracle rounds on the higher side to 160K (5 32K extents).

    Hence, storage could be used to allocate the initial size for an object. The Default Storage clause cannot be specified for LMTs at tablespace level.

    SQL> create tablespace users4
      2  datafile 'D:\oracle\oradata3\users4.dfb' size 5M
      3  autoextend off
      4  extent management local uniform size 32K
      5  default storage(initial 100k next 100k minextents 2 maxextents unlimited pctincrease 50);
    create tablespace users4
    *
    ERROR at line 1:
    ORA-25143: default storage clause is not compatible with allocation policy
    

    Please refer the example section for LMT creations and migration examples.

DICTIONARY MANAGED TABLESPACE를 사용 대비, LOCALLY MANAGED TABLESPACE를 사용하는 것의 장점 [OTN]

제품 : ORACLE SERVER

작성날짜 :

DICTIONARY MANAGED TABLESPACE를 사용 대비, LOCALLY MANAGED TABLESPACE를 사용하는 것의 장점
===========================================================================================

PURPOSE


이 문서는, Locally Managed Tablespace에 대한 설명과 함께,
Dictionary Managed Tablespace 대비, 장점을 기술하는 데 목적이 있다.

Explanation


1. LOCALLY MANAGED TABLESPACE

Locally Managed Tablespace는, 자체 extent에 대한 관리를 각각의 데이터 파일에
비트맵 형식으로 저장하여 관리하는 테이블스페이스로, 데이터 파일을 구성하는
블럭이 비어 있는지, 사용 중인지에 대한 정보를 관리한다.
비트맵의 각각의 비트는, 하나의 블럭 또는 블럭의 그룹에 해당하는 정보를 나타낸다.

익스텐트가 할당되거나, 비워지거나, 재사용될 때, 오라클에서는 블럭의 새로운
상태를 나타내기 위해 비트맵의 값을 변경한다. 이와 같은 변경사항은 기본 방식인
Dictionary Managed Tablespace와는 달리,
rollback 정보를 생성하지 않는데, 이것은 데이터 딕셔너리의 테이블을 갱신하지
않기 때문이다 (테이블스페이스 별 quota 정보는 제외).

1) 공간 정보 관리를 위한 내부 작업을 줄임.
2) 데이터 딕셔너리 테이블에 대한 경합 감소됨.
3) 익스텐트 관리와 관련된 관련 rollback 생성이 되지 않음.
4) Coalescing 이 불필요함.

2. 테이블스페이스의 공간 관리

1) 사용되지 않는 익스텐트 정보가 비트맵에 의해 관리됨.
(따라서, 테이블스페이스의 일부분이 비트맵 정보를 저장하는 데 사용됨)
2) 각 비트는, 블럭이나, 블럭의 그룹의 정보를 나타냄.
3) 비트 정보는, 사용 중인지, 그렇지 않은지를 나타냄.
4) DBA_EXTENTS 나 DBA_FREE_SPACE 등의 뷰는 동일하게 사용됨.

3. 구문 규칙

EXTENT MANAGEMENT 절의 LOCAL 옵션을 사용하면, 테이블스페이스가
Locally Managed 방식으로 생성된다.

Extent_management_clause:
[EXTENT MANAGEMENT
{DICTIONARY | LOCAL
{AUTOALLOCATE | UNIFORM [SIZE integer M] }}

옵션 설명:

DICTIONARY 테이블스페이스에 대해 Dictionary Table를
사용하여 공간 정보를 관리함. ( 기본 값 )
LOCAL 테이블스페이스가에 대해 비트맵을 사용하여
Locally Managed 방식으로 공간 정보를 관리함.
AUTOALLOCATE 테이블스페이스에 대한 익스텐트 관리를 시스템에서
관장하도록 함.
(사용자는 익스텐트의 크기를 수동으로 지정할 수 없음)
UNIFORM 테이블스페이스가 동일한 크기의 익스텐트로 구성되도록
지정함. 크기는 기본적으로 바이트 단위로 지정
( 익스텐트 크기를 KB 또는 MB 단위로 지정하기 위해서는
K 또는 M 을 사용하여 지정)
이 옵션을 사용하게 되면, DEFAULT Storage 절,
MINIMUM EXTENT 또는 TEMPORARY 옵션을 사용할 수 없다.

Oracle 9.2 이전 버젼에서는, EXTENT MANAGEMENT 절을 SYSTEM 테이블스페이스를
제외한 permanent tablespace나, temporary tablespace 생성 시 지정할 수 있었다.
Oracle 9.2부터는 SYSTEM 테이블스페이스를 포함한 모든 테이블스페이스를
Locally managed 방식으로 생성할 수 있다.
CREATE DATABASE에서 EXTENT MANAGEMENT LOCAL 절을 사용하게 되면,
오라클에서는 SYSTEM 테이블스페이스를 Locally Managed Tablespace로 생성하며,
익스텐트의 크기는 오라클에서 결정하게 된다. 이 기능을 사용하기 위해서는,
COMPATIBLE 옵션이 9.2 또는 그 이상으로 지정되어 있어야 한다.
Locally Managed SYSTEM tablespace는 기본적으로 AUTOALLOCATE 방식을
사용하게 되며, Locally Managed 방식으로 SYSTEM 테이블스페이스를 생성할 때,
UNIFORM extent 크기를 지정할 수 없다.

다음 storage parameter(NEXT, PCTINCREASE, MINEXTENTS, MAXEXTENTS)와
DEFAULT STORAGE는 Locally Managed Tablespace에서는 사용할 수 없다.

테이블스페이스 생성 후에는 공간관리 방법을 변경할 수 없다.

Oracle 8.1.6부터, Dictionary Managed Tablespace를 Locally Managed
Tablespace로 마이그레이션을 할 수 있으나, 8.1.5에서는 그와 같은 작업을
수행할 수 없다.

기존에 사용해 왔던 테이블스페이스를 Locally Managed 방식으로 전환
시키기 위해서는 DBMS_SPACE_ADMIN.TABLESPACE_MIGRATE_TO_LOCAL 프로시져를
사용하면 된다.
그리고, DBMS_SPACE_ADMIN 패키지는 Locally Managed Tablespace를
관리하는 데 필요한 각종 프로시저를 제공한다.

다음 문장에서, 데이터베이스 블럭 크기가 2K인 것을 가정하였다.

CREATE TABLESPACE tbs_1
DATAFILE 'file_tbs1.dbf' SIZE 10M
EXTENT MANAGEMENT LOCAL
UNIFORM SIZE 128K

위 문장을 수행시키면, Locally Managed Tablespace를 생성하며,
모든 extent의 크기를 128K로 할 때, 비트맵의 각 비트는 64개 블럭에 대한
정보를 나타낸다. 기본적으로, 데이터베이스 블럭의 크기의 기본값을 2K로
가정하였을 때, 각 비트는 하나의 extent(128K)에 대한 정보를 나타내므로,
각 비트맵은 64개의 오라클 블럭을 필요로 하게 된다.

( 64 블럭 = 128K UNIFORM SIZE / 2K ORACLE BLOCK SIZE )

4. Dictionary Managed Tablespace를 사용하는 것 대비, Locally Managed
Tablespace를 사용하였을 경우의 장점

1) Locally Managed Tablespace는 가용한 공간에 대한 정보를 데이터
딕셔너리에 저장하지 않으므로, 데이터 딕셔너리에 대한 경합을
줄이게 된다.

2) Extent에 대한 local management를 통해, 인접한 가용 공간의 정보를
자동으로 관리하게 되므로, 가용 extent에 대한 coalesce 작업을
수행하지 않아도 된다.

3) 공간 관리를 위한 데이터베이스 내부 처리 작업을 피할 수 있다. 반면
이와 같은 내부 처리 작업은 Dictionary Managed Tablespace에서는
필요한데, extent를 사용하거나, 반납을 하는 등의 작업이 발생할 때마다
rollback segment나 데이터 딕셔너리 테이블의 공간을 사용하거나
반납하는 등의 작업이 필요하게 된다.

4) Locally Managed Tablespace에서의 extent의 크기는, 시스템에 의해
자동적으로 관리된다. 반면, 모든 extent는 Locally Managed Tablespace
에서는 동일한 크기를 사용하게 할 수도 있다.

5) Extent에 대한 정보를 나타내는 비트맵의 변경 사항은 rollback 정보를
생성하지 않는다. 이것은, 데이터 딕셔너리의 정보를 변경하지 않기
때문이다. ( 테이블스페이스에 대한 quota 정보와 같은 일부 사항은
예외 )

6) Fragmentation을 줄일 수 있다.

Example


Reference Documents


<Note:93771.1> Locally Managed Tablespace in Oracle 8i
<Note:103020.1> Migration from Dictionary Managed to Locally Managed
Tablespaces
<Note:105120.1> Advantages of Using Locally Managed vs Dictionary Managed Tables
paces


http://kr.forums.oracle.com/forums/thread.jspa?threadID=477238&tstart=15 

2009년 7월 26일 일요일

lseek 를 이용한 파일 조작

출처 : http://cafe.daum.net/info0U/MBKS/19?docid=1F4gb|MBKS|19|20090512140641&q=lseek&srchid=CCB1F4gb|MBKS|19|20090512140641 



2.3.4절. 예제코드

다음은 실제 작동되는 예제 코드이다. 어려운 내용은 없음으로 주석으로 대신하도록 하겠다. 아래코드는 학습목적으로 연습삼아 만든 코드이다. 에러처리, 입출력검사, 코드 효율성, 인터페이스 등은 염두에 두지 않은 코드이다. 숨어있는 버그를 잡거나 깨끗하게 코드를 다시 만들어 보는것도 많은 도움이 될것이다.

예제 : seek_db.c

#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

// DB 포맷 확인용
#define APPNAME "MYDB"

// ANSI 코드 
// 스크린 지우기
#define SCR_CLEAR printf("^[[2J")
// x,y 좌표로 커서이동하기
#define MOVE_CURSOR(x, y) printf("^[[%d;%dH", x, y)

// 메시지를 출력하고 사용자 입력을 기다린다. 
// 스크린 지우기 전에 메시지를 확인할 목적으로 
// 사용된다. 
#define WAIT_INPUT(x) printf("%s", x);getchar() 

// 개행문자 제거
#define chop(str) str[strlen(str)-1] = '\0'; 

// 헤더의 크기 정의  
// 헤더는 recode 를 제외한 파일의 가장앞에 있는 
// 정보이다. 

// DB 포맷 정보 크기 
#define DBINFO_SIZE strlen(APPNAME) 
// R_NUM,INC_NUM 크기
#define INDEX_SIZE sizeof(int)*2 
// 전체 헤더 크기
#define HEADER_SIZE INDEX_SIZE + DBINFO_SIZE

// 메인메뉴 
char *menu =
"
데이타수 : %d 
====================
1. 리스트 보기
2. 리스트 추가
3. 리스트 삭제 
4. 종료
==================== 
input : ";

// 레코드 입력 메뉴
char *input_menu = 
"
번   호  :
이   름  :
전화번호 : 
";

// 레코드 구조체
typedef struct _data
{
    int  num;          // 일련번호
    char name[16];       // 이름
    char tel_num[16];  // 전화번호
} Data;

//  R_NUM, INC_NUM
typedef struct _index_num
{
    int datanum;    // R_NUM   : 데이타 총갯수
    int incnum;     // INC_NUM : 데이타 일련번호 
} Index_num; 


// Index_num 값 즉 R_NUM 과 INC_NUM 
// 을 얻어온다.  
Index_num get_indexnum(int fd)
{
    Index_num index_num;
    lseek(fd, DBINFO_SIZE, SEEK_SET);
    read(fd, (void *)&index_num, HEADER_SIZE);
    return index_num;
}

// DB 파일을 체크한다. 
// 파일의 처음 4바이트 문자가 APPNAME 과 같으면 참 
int dbcheck(fd)
{
    char dbname[8];
    memset(dbname,0x00,8); 
    read(fd, dbname, 8);
    if (strncmp(dbname, APPNAME, DBINFO_SIZE) ==0) 
        return 1;
    else
        return -1;
}

// 최초에 DB파일이 생성되지 
// 않았을때 DB 파일을 초기화 시켜준다. 
// DB 포멧정보(APPNAME)이 들어가고 R_NUM, INC_NUM
// 은 0으로 초기화 된다. 
int init_datanum(int fd)
{
    Index_num index_num;
    write(fd, APPNAME, DBINFO_SIZE);
    memset((void *)&index_num, 0x00, INDEX_SIZE);
    write(fd, (void *)&index_num, INDEX_SIZE); 
}

// 레코드가 insert 되었을 경우
// R_NUM과 INC_NUM 을 증가시킨다. 
int inc_indexnum(int fd)
{
    int datanum;
    Index_num index_num;
    index_num = get_indexnum(fd);
    index_num.datanum++;
    index_num.incnum++;
    lseek(fd, DBINFO_SIZE, SEEK_SET);
    write(fd, (void *)&index_num, INDEX_SIZE);
    return 1;
}

// 메인 메뉴를 출력한다. 
void print_main_menu(fd)
{
    Index_num index_num;

    index_num = get_indexnum(fd);
    printf(menu,index_num.datanum);
}

// 서브메뉴를 출력한다. 
void print_menu(char *sub_menu)
{
    printf(sub_menu);
}

// 레코드를 삽입한다. 
// 레코드 삽입위치는 파일의 마지막이다. 
void input_data(Data mydata, int fd)
{
    // 파일의 마지막으로 이동 
    lseek(fd, 0, SEEK_END);
    write(fd, (void *)&mydata, sizeof(Data));
    inc_indexnum(fd);
}

// 레코드 리스트를 출력한다. 
void print_data(int fd)
{
    int i;
    int offset = 0;
    Data list;
    Index_num index_num;
    index_num = get_indexnum(fd);

    // 레코드의 시작위치로 이동한다. 
    lseek(fd, HEADER_SIZE, SEEK_SET);
    for (i = 0; i < index_num.datanum; )
    {
        read(fd, (void *)&list, sizeof(Data));
        if (list.num > 0) 
        {
            i++;
            printf("%3d %16s %16s\n", list.num, list.name, list.tel_num); 
        }
    }    
}

// 레코드를 삭제한다. 
// 실제로 데이타를 삭제하지는 않으며 
// Data.num 에 (-1)을 곱해준다. 
int del_data(int fd,int num)
{
    int offset;
    int del_flag;
    Data list;
    Index_num index_num;

    index_num = get_indexnum(fd);
    printf("delete num is %d\n", num);

    // 입력된 번호가 1 보다 작거나 레코드 수보다 클경우
    if ((index_num.incnum-1) > index_num.incnum || num < 1)
        return -1;

    // 삭제하고자 하는 레코드의 위치로 이동한다. 
    offset = (sizeof(Data)*(num-1)) + HEADER_SIZE;
    lseek(fd, offset, SEEK_SET);

    read(fd, (void *)&list, sizeof(Data));
    if (list.num < 0) 
    {
        printf("list.num is : %d\n", list.num);
        return -2;
    }

    del_flag = list.num*(-1);    

    // 삭제하고자 하는 레코드의 위치로 이동해서 
    // list.num*(-1) 값을 입력한다. 
    lseek(fd, offset, SEEK_SET);
    write(fd, (void *)&(del_flag), sizeof(int));

    // R_NUM 을 1 감소시킨다. 
    lseek(fd, DBINFO_SIZE, SEEK_SET);
    index_num.datanum--;
    write(fd, (void *)&(index_num), INDEX_SIZE);
    return 1;
}

// 메뉴선택에 대한 처리
void sel_menu(fd)
{
    char menu_num; 
    Index_num index_num;

    // R_NUM과 INC_NUM 을 구해온다. 
    index_num = get_indexnum(fd);

    while(1)
    {
        Data mydata;
        char buf[11];
        char num[11];
        int  state;
        char data[16];

        SCR_CLEAR;            // 화면 clear    
        MOVE_CURSOR(1,1);     // 커서이동
        print_main_menu(fd);  // Main 메뉴출력
        fgets(num, 11, stdin);

        // 입력번호에 따라 분기한다. 
        switch(atoi(num))
        {
            // 리스트 출력
            case 1 :
                print_data(fd);
                WAIT_INPUT("Press any key!!");
                break;
            // 입력 
            case 2 :
                SCR_CLEAR;

                MOVE_CURSOR(1,1);
                print_menu(input_menu);

                MOVE_CURSOR(2,12);
                printf("%d", ++index_num.incnum);
                mydata.num = index_num.incnum;

                MOVE_CURSOR(3,12);
                fgets(mydata.name, 16, stdin);
                chop(mydata.name);

                MOVE_CURSOR(4,12);
                fgets(mydata.tel_num, 16, stdin);
                chop(mydata.tel_num);
                input_data(mydata, fd);

                WAIT_INPUT("Press any key!!");
                break;

            // 삭제 
            case 3 :
                MOVE_CURSOR(10,1);
                printf("삭제번호 ");
                fgets(buf, 11, stdin);
                state = del_data(fd, atoi(buf));
                if (state < 0)
                {
                    printf("잘못된번호 선택\n");
                }
                WAIT_INPUT("Press any key!!");
                break;
            case 4 :
                printf("bye bye\n");    
                exit(0);
            default :
                break;
        }
    }
}



int main(int argc, char **argv)
{
    int data_num = 0;
    int is_fileok = 0;
    int fd;
    Data mydata;

    if (argc != 2)
    {
        printf("Usage : ./seek_db dbfile\n");
        exit(0);
    } 

    if ((access(argv[1], F_OK) == 0))
    {
        is_fileok = 1;
    }

    fd = open(argv[1], O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
    if (fd < 0)
    {
        perror("error : ");
        exit(0);
    }
    if (is_fileok == 0)
    {
        printf("FILE INIT\n");
        init_datanum(fd);
    }
    else
    {
        if (dbcheck(fd) != 1)
        {
            fprintf(stderr, "%s 는 잘못된 DB 파일입니다\n", argv[1]);  
            exit(0);
        }
    }

    sel_menu(fd);

    close(fd);
    return 1;
}
				

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


2009년 3월 4일 수요일

ITL Entry

ITL(Interested Transaction List)은 특정 블록을 변경하고자 하는 트랜잭션의 목록을 의미하며, 블록의 헤더에서 그 정보를 관리한다. 블록을 변경하고자 하는 모든 트랜잭션은 블록 헤더의 ITL의 엔트리 중 하나로 자신을 등록해야 한다. 만일 ITL이 약속된 최대치, 즉 MAXTRANS에 의해 지정된 값을 초과하거나 블록 내의 여유공간이 부족해서 엔트리를 등록하는 것이 불가능한 경우, 프로세스는 이미 ITL에 엔트리를 등록한 프로세스가 Exclusive하게 획득한 TX 락을 Shared 모드로 획득하기 위해 대기하게 된다. 이때의 대기현상은 enq: TX - allocate ITL entry 이벤트로 관찰된다. 테이블을 생성할 때 부여하는 세가지 속성값이 ITL에 영향을 준다.

  • INITRANS : 블록 헤더마다 몇 개의 ITL 엔트리를 미리 확보할 지를 결정한다. 가령 INITRANS의 값을 10으로 주면 10개의 동시 트랜잭션을 위한 공간이 마련된다.
  • MAXTRANS : 최대 몇개의 ITL 엔트리를 허용할지를 결정한다. 가령 MAXTRANS의 값을 50으로 주면 최대 50개까지의 동시 트랜잭션을 허용한다. MAXTRANS의 기본값은 255이며, 오라클 10g부터는 MAXTRANS는 255로 고정된다. 즉, MAXTRANS 값을 지정해도 오라클은 이 값을 무시하며 항상 255의 값을 사용한다.
  • PCTFREE : 블록이 최초 생성될 때는 INITRANS에 지정된 값만큼 ITL 엔트리가 확보되었다가, 동시 트랜잭션이 증가하면 PCTFREE로 확보된 영역 내에서 MAXTRANS만큼 추가로 확장된다.

만일 동시 트랜잭션이 왕성할 것으로 예상되는 테이블이라면 INITRANS를 충분히 주는 것이 좋다. INITRANS를 충분히 주면 동적으로 공간을 확보하는 오버헤드가 줄어들며, ITL 엔트리 부족에 따른 TX 락 경합이 발생할 확률도 줄어든다. 오라클 10g부터는 MAXTRANS의 값이 255로 고정되므로 잘못된 MAXTRANS 값 지정으로 인해 성능 문제가 생길 소지가 사라졌다. PCTFREE 값을 비정상적으로 작게 설정하는 경우 ITL을 동적으로 확장할 여유공간이 부족해서 문제가 될 수도 있다. 오라클 9i에서 MAXTRANS 값을 임의로 작게 준 경우 TX 락 경합이 어떻게 발생되는지 살펴보자.(오라클 9i에서 테스트를 하는 이유는 오라클 10g에서는 MAXTRANS가 255로 고정되었기 때문이다)

SQL> -- maxtrans 값을 2 로 준다. 이 경우 세개 이상의 세션이 동시에 블록을 변경하지 못한다.
create table tx_itl_test(id number)
initrans 2 maxtrans 2;

-- 총 5건의 데이터를 생성한다.
insert into tx_itl_test values(1);
...
insert into tx_itl_test values(5);
-- dbms_rowid package를 이용해서 같은 블록안에 들어가 있는 로우를 확인한다.
 
SQL> select id, dbms_rowid.rowid_block_number(rowid) from tx_itl_test;

       ID DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
---------- ------------------------------------
        1                               175362
        2                               175362
        3                               175363
        4                               175363
        5                               175363
id값이 3, 4, 5 인 로우들이 175363번 블록에 있으므로 이 세개의 로우에 대해 동시에 다른 세션들에서 업데이트를   시도한다.

세션A:(SID = 27)
SQL> update tx_itl_test set id = 3 where id = 3;
1 row updated.

세션B:(SID = 10)
SQL> update tx_itl_test set id = 4 where id = 4;
1 row upated.

세션C:(SID = 40)
SQL> update tx_itl_test set id = 5 where id = 5;
..... Wait ...................................................


세번째 업데이트를 수행한 10번 세션이 대기상태에 빠진다. 이 상태에서 V$LOCK 뷰를 통해 TX 락 경합이 어떻게 발생하는지 확인해보자

SQL> exec print_table('select * from v$lock where type = TX');
ADDR                           : C00000001EBA58B0
KADDR                         : C00000001EBA5A28
SID                              : 10
TYPE                             : TX
ID1                              : 65548
ID2                              : 58643
LMODE                         : 6    ? TX 락을 Exclusive하게 획득중
REQUEST                      : 0
CTIME                          : 33
BLOCK                         : 0
-----------------
ADDR                          : C00000001EB9DCE8
KADDR                        : C00000001EB9DE60
SID                             : 27
TYPE                           : TX
ID1                             : 131072
ID2                             : 68746
LMODE                        : 6    ? TX 락을 Exclusive하게 획득중
REQUEST                     : 0
CTIME                         : 43
BLOCK                        : 1
-----------------
ADDR                          : C00000001F1EFEE0
KADDR                        : C00000001F1EFF00
SID                             : 40
TYPE                           : TX
ID1                             : 131072
ID2                             : 68746
LMODE                        : 0
REQUEST                     : 4        <-- Shared 모드로 TX 락을 획득하기 위해 대기
CTIME                         : 14
BLOCK                        : 0

테스트) ITL 엔트리 부족에 의한 TX 락 경합

위의 테스트 결과를 분석하면 성공적으로 update를 수행한 27번 세션과 10번 세션은 자신의 트랜잭션에 해당하는 TX 락을 Exclusive하게 획득하는데 성공했음을 알 수 있다. 문제의 40번 세션은 최초에 Update를 수행한 27번 세션이 획득한 TX 락(ID1=131072, ID2=68746)을 Shared 모드(REQUEST=4)로 획득하기 위해 대기하고 있다. Unique Key 충돌에 의한 TX 락 경합과 ITL 엔트리 부족에 의한 TX 락 경합 사이에는 미묘한 차이점이 있다. Unique Key 충돌의 경우, 대기세션은 자신만의 TX 락을 Exclusive하게 획득한 상태에서 이전 세션이 획득한 TX 락을 Shared 모드로 획득하기 위해 대기한다. 반면 ITL 부족의 경우, 대기세션은 자신만의 TX 락을 획득하기 전에 이전 세션이 획득한 TX 락을 Shared 모드로 획득하기 위해서 대기한다. 이러한 차이는 블록을 변경하기 전에 먼저 ITL 엔트리를 등록해야 하는 데서 비롯된다. ITL 엔트리 부족에 따른 TX 락 경합 현상은 일반적인 상황에서는 잘 생기지 않는다. 동시에 수십 ~ 수백 개의 세션이 하나의 블록에 대해 서로 다른 로우를 변경하는 경우는 매우 드물기 때문이다. 다만, Row Chaining이나 Row Migration이 발생한 로우의 경우 하나의 로우를 업데이트할 때 여러 개의 블록에 대해 각각 ITL 엔트리를 할당해야 하므로 이로 인해 ITL엔트리 부족에 의한 TX 락 경합이 발생할 확률이 높아진다.

[편집]Parameter & Wait Time

[편집]Wait Parameters

  • P1 : Enqueue 정보
  • P2 : usn<<16 | slot
  • P3 : sequence

[편집]Wait Time

enqueue 대기이벤트와 동일하다. 최대 3초까지 기다린다. 만일 TX 락을 획득하기 못하면 획득할 때까지 대기한다

[편집]Check Point & Solution

[편집]높은 INITTRANS 속성 부여

높은 INITRANS 값으로 테이블을 생성하는 것이 ITL 공간 부족에 따른 TX 락 경합 현상을 해결하는 일반적인 방법이다. 더욱 중요한 것은 잘못된 어플리케이션 설계로 불필요한 경합이 발생되는 것인지 판단하는 것이며, 잘못된 설계에 의한 것이라면 어플리케이션을 수정하는 것만이 유일한 해결책이다.

V$SEGMENT_STATISTICS 뷰를 이용하면 어떤 세그먼트에 대해서 ITL 부족에 의한 경합이 많이 발생하는지 확인할 수 있다. STATISTIC_NAME = 'ITL waits' 조건을 이용하면 어떤 세그먼트에 대해 ITL 부족 문제가 많이 발생했는지 알 수 있다.

[편집]Event Tip

[편집]Analysis Case


출처 : http://wiki.ex-em.com/index.php/Enq:_TX_-_allocate_ITL_entry