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;
}