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년 6월 7일 일요일

DBMS는 소프트웨어의 기본, OS부터 충실히 공부해야

http://www.ittoday.co.kr/home/post/post_view.jsp?dseq_no=7943&menuId=ABAA&cateCode=ABAA&pageCode=7943&curPage=&serType=&serText=

[멘토를 찾아서](1)김기완 알티베이스 대표
"DBMS는 소프트웨어의 기본, OS부터 충실히 공부해야"
“데이터베이스관리시스템(DBMS)는 모든 소프트웨어의 기본입니다.”

김기완 알티베이스 대표의 DBMS 사랑은 남다르다. 20여년을 DB분야에서만 있었으니 최고의 DB전문가라고 할 수 있을 것이다. 특히 국산 DBMS를 직접 개발하는 회사까지 차렸으니 DB분야에서 DBA부터 개발, 컨설팅, 영업 등 할 수 있는 역할은 다 해봤으리라.

‘멘토를 찾아서’라는 기획을 위해 시간을 내달라고 했을 때 김 대표는 흔쾌히 허락했다. DBMS 분야에 뛰어들기 위해 몇 년간 준비하고 있다는 말에 업계 선배로서 바쁜 와중에도 시간을 내준 것. DB를 공부하려는 후배들을 위해 인터뷰를 허락하는 것은 당연하다고 했다.

김 대표의 첫 인상은 푸근했다. 오라클에 맞서 10여년을 버텨 온 알티베이스 수장으로서의 강한 카리스마 대신 학교에서 볼 수 있는 선배의 모습이었다. 안경너머로 보이는 눈빛이 진지함과 호기심으로 가득 차 있는 듯했다. 벤처 CEO다운 도전 정신이 어디에서 나왔을까 하는 생각이 들 정도였다.

김 대표는 "DBMS 분야로 진출하고 싶은 학생들이나 초보 개발자에게 꼭 필요한 것은 운용체계(OS)에 대한 공부"라고 조언했다. 운용체계를 배우면 DBMS에 대한 기본지식을 쌓을 수 있다는 것이다. 그는 이어 "컴파일러, 자료구조, 파일처리 3가지를 배우면 DBMS를 알기가 쉽다"고 설명했다.

김 대표는 DBMS 분야로 취업을 희망하는 학생들에 대한 조언으로 학생으로서 기본에 충실하고 개발 능력을 키우는 것이 중요하다고 했다. DBMS가 네트워크, 운용체계, 자료구조 등 모든 컴퓨터 시스템과 관련돼 있으니 그에 대한 전반적인 지식을 쌓는 것도 필요하다고 했다.

그는 무엇보다 창의성을 갖추는 것도 중요하다고 강조했다. 풀어가는 과정이 다 다를 수 있는데, 오히려 다른 방식으로 문제를 풀어가는 것을 보면 직원으로 채용하고 싶을 정도로 매력을 느낀다고 말했다.

알티베이스의 개발자 채용 방식은 독특하다. 출신학교 등의 선입관을 배제하고 면접을 보는 과정에서 문제를 내고, 직접 칠판에 나와 풀게 하는 방식으로 진행된다. 정확한 답도 중요하겠지만 그 답을 찾아내는 과정도 중요시한다. 기본기와 창의성을 강조하는 대목이다.

김 대표는 경쟁을 즐기고 있는 것 같았다. 한국오라클이라는 탄탄대로의 직장을 과감히 그만두고 누구나 반대했던 국산 DBMS개발 회사 창업을 할 때도 그랬단다. 또한 매년 적절한 계획 속에 투자를 하면서 시장을 공략했을 때도 그랬다.

무엇보다 그는 최근 국산 DBMS가 많이 나오는 것에 대해서도 적극적으로 찬성했다. 경쟁이란 것이 필수적으로 필요하다는 논리 때문이다. 삼성이 해외에서 1위를 휩쓰는 배경에는 LG전자와의 경쟁이 크게 한 몫 했다는 얘기다. 그는 티맥스소프트, 큐브리드 등 국산 DBMS가 많이 생기는 것에 대해서는 적극 찬성하고 같이 시장을 키워야 한다고 하면서도 오히려 일회성으로 그쳐서는 안 된다는 점을 강조했다. 모두 기본에 충실해 고객에게 기쁨을 줄 때만이 지속적으로 생존가능성이 있다는 것이다.

대학생과 예비개발자, 초보 개발자에게 그는 ‘기본을 갖추라’고 강조했다. 스스로도 대학 시절 공부를 많이 하지 못해 대학원까지 가서 공부했다면서, 지금 현재 있는 상황에서 최선을 다해 기본기를 갖추기 위해 노력을 해야 한다고 말했다.

다음은 김기완 대표와의 일문일답.

- DBMS로 오라클 등 글로벌 기업과 경쟁하기 힘든 데, 과감하게 창업을 하게 된 동기는?

"주위의 많은 반대에도 불구하고 창업을 하게 된 이유는 창업에 대해 성공하리라는 자신감과 재미가 있었기 때문이다. 게다가 한국오라클에서 7여년간 일하면서 많은 기술을 알고 있었기 때문에 더욱 확신이 있었다. 국산 DBMS의 활성화, 한국 오라클의 아성을 넘어보는 것이 앞으로의 목표다."

- 알티베이스에 대해 자랑한다면?

"소프트웨어에 대해 배우고자 하는 학생, 개발자라면 우리 회사가 적격이다. DB와 관련한 많은 레퍼런스 소스(reference source)가 있어 어려움을 겪을 때 참고 할 수 있고, 중소기업이기 때문에 다양한 경험을 쌓을 수 있다. 그러나 외국회사에서 와서 벤치마킹 할 정도로 회사 업무들이 체계화 돼있고 프로세스가 잘 정립돼 있기 때문에 DB 를 배우려 하는 학생 뿐 아니라 소프트웨어 업무에 관심이 있는 학생들 모두에게 많은 이득이 될 것이다. 소프트웨어를 체계적으로 배울 수 있는 곳이 알티베이스라고 자신한다."

- 해외 진출 계획은?

"현재 활발하게 해외 진출을 준비 중이다. 일단, 일본과 중국에 진출할 생각인데 우리나라에서 검증된 제품으로 천천히, 꾸준히 도약할 생각이다. 현재 알티베이스의 DBMS 버전이 5인데, 많은 기업에서 쓰고 있는 안정화된 버전인 알티 4 로 런칭할 것이다."

- 티맥스소프트와 큐브리드 역시 DBMS를 만드는 기업이다. 한국 최초의 국내 DBMS를 만든 기업으로써 이들과의 경쟁에 대해 어떻게 생각하는가?

"경쟁이란 것은 필수적으로 필요한 것이다. 삼성의 제품들이 해외에서 1위를 휩쓰는 이유가 무언지 아는가? 바로 LG가 있었기 때문이다. 서로 경쟁하면서 지금의 삼성을 만든 것이다. 알티베이스 역시 이들과 선의의 경쟁을 통해 글로벌 업체가 될 것이다."

- 알티베이스, 티맥스소프트, 큐브리드 등 국산 DBMS의 바람이 거세다. 최근에는 공공기관에도 외국의 DBMS를 제치고 수주권을 따냈는데, 이러한 국산 DBMS의 선전을 어떻게 생각하는가.

"바람직한 현상이다. 그러나 일시적인 현상으로 그치면 절대 안된다. 꾸준하게 점진적으로 발전해야 하며, 시장 규모도 키워야 한다. 고객과의 관계에서 가장 중요한 신뢰를 잘 유지하는 것이 도움이 될 것이다."

- 대학을 졸업하고 나서 가장 힘들었던 시기는?

"첫 직장이었던 삼성종합기술원에서 근무했었을 때다. 삼성종합기술원은 당시 IT사관학교로 불릴 정도로 교육을 잘 시키는 곳으로 유명했다. 슈퍼 컴퓨터관련 업무를 했었던 그곳은 너무 체계적이고 권위를 강조하던 곳이었다. 개인적으로는 그 문화에 적응하기가 가장 힘들었다. 그래서 1년 반을 지내고 나서 한국오라클로 옮겼다. 그러나 많은 것을 배울 수 있어 좋은 기회였던 것은 분명한 사실이다."

-한국 오라클에서 어떤 일을 했으며 자신에게 도움이 많이 되었나?

"1993년에 오라클에 입사를 했는데, 그때는 한국 오라클이 설립 초창기였을 때였다. 그래서 사원수가 적어 혼자서 다양한 분야의 일을 하게 되었는데, 그렇게 해서 많은 업무를 배울 수 있었다. 그때 수 십명에서 시작한 오라클이 8년 뒤 퇴사 할 때는 사원수가 800여명으로 늘어나 있었다."

- 그렇다면, 현재 취업을 앞둔 대학생들에게도 한국오라클이 매력적인가?

"시각에 따라 다르게 볼 수가 있다. 이전과 비교할 때 적어도 현재의 한국오라클은 성장 속도가 많이 둔화됐다. 업무도 체계적으로 시스템화돼 있기 때문에 한 분야만 배울 수 있지 전체 흐름을 배우기는 어려운 구조다. 그래서 많은 것을 배워야 하는 신입에게 추천하고 싶지는 않다. 물론 개발이라는 측면을 강조한 얘기다."

- 선배들의 말을 들어보면, 한국에 있는 외국계 기업들은 대부분의 업무가 세일즈나, 고객응대 파트라고 하는데, 사실인가?

"회사는 장사를 하는 곳이므로, 그것은 당연한 것이다. 게다가 본사는 미국에 있으므로 다른 나라에는 그 나라를 지원해줄 정도면 된다. 물론 한국오라클에도 엔지니어가 있긴 하지만 깊이 있는 지식을 갖기는 힘들 것이다. 그런 측면에서 보면 국내 기업들이 강점이 많다. 소스코드도 직접 볼 수 있고 개발을 하면서 많은 것을 배울 수가 있다."

        김 대표와 양지웅 대학생 명예기자가 기념촬영 하고 있다.

-요즘은 높은 실업난과 더불어 많은 대학생들이 대학원을 생각한다. 공대생이 대학원에 가서 더 공부를 하는 것에 대해 어떻게 생각하나?

"대학원, 좋다. 대학원에서는 대학에서 배우지 못한 것을 배울 수 있다. 체계적인 업무, 각종 문서와 논문을 쓰면서 어떻게 써야 논리적으로 글을 쓸 수 있는가에 대해서도 익힐 수 있다. 게다가 학업기간도 경력으로 쳐주니 공부를 더 하고 싶은 학생이라면 추천한다."

- 알티베이스는 현재 성균관대와 연계해 DBMS 임베디드 석사 과정을 만들어 진행하고 있는데.

"인재를 발굴하기 위해 성대와 협약을 맺어 하는 프로그램으로, 우리 회사에서 석사 과정의 비용을 지원해주고 졸업 후 입사하는 제도다. 자격은 DB분야를 모른다 하더라도 DB관련 업무를 꼭 하고 싶은 사람, 재미있어 하는 사람이면 좋다. 필기 시험을 보기 때문에 개발에 대한 지식이 있으면 더욱 가능성이 높아진다. 현재 시행한지 1년 정도 지났는데, 학기 중에는 공부를 하고, 주마다 한 번씩의 기술 세미나와 방학 중에 인턴을 하게 된다. 현재 학생 수는 10명으로 모두 열정과 열기가 대단하다."

- DB컨설턴트가 되고 싶은 학생에게 조언 한다면?

"컨설팅은 다른 회사에 가서 자기의 생각을 파는 일종의 ‘장사’ 이므로 말의 표현력과 재치가 가장 중요하다. 한 분야가 아닌 많은 분야에 대한 해박한 지식을 가지고 있어야 함은 물론이다."

-DB 분야로 인생의 진로를 정하고 싶은 대학생들이나 초보 개발자들이 무엇을 더 배우면 좋을까?

"뭐니뭐니 해도 운용체계(OS)에 대한 공부가 필수다. DBMS는 DB에 대한 운용체계라 할 수 있다. 그 운용체계 또한 윈도 같은 운용체계와 같이 내부적으로 구성돼 있으므로, 운용체계를 배우면 DBMS에 대한 기본 지식을 쌓을 수 있다. 또한, 컴파일러와 자료구조, 파일처리를 배워두면 좋다. 그 위에 실무 능력을 겸비해야 진정한 실력자라 할 수 있다."

- DB 관련 분야 취업을 희망하는 학생이나 초보 개발자에게 한마디 조언 한다면.

"일단은, 그것을 정말로 좋아해야 한다고 생각한다. 아무리 회사가 좋아도 자신이 좋아하는 일이 아니라면, 회사나 자신에게나 독이 된다. 학생의 신분으로서 기본에 충실해야 하고 개발 능력을 키우는 것도 필요하다. 프로그램 코드가 1000라인에서 10000라인으로 늘어난다면 그것은 단순하게 라인 수만 증가하는 것이 아님을 명심해야 한다. 더불어, DB라는 것이 네트워크, OS, 자료구조 등 모든 컴퓨터 시스템과 관련돼 있으니 그에 대한 전반적인 지식도 쌓으면 더욱 좋겠다."

<양지웅 대학생 명예기자 jiwoong.com@gmail.com >

’멘토를 찾아서’ 기획시리즈는 대학생 명예기자 혹은 온라인 기자가 IT업계의 뛰어난 멘토들을 직접 방문해 소개하는 코너로 개발자 포털 데브멘토(www.devmento.co.kr)와 아이티투데이가 공동기획해 연재합니다.

2009년 5월 30일 토요일

외산 DBMS 불만은 ‘높은 유지보수비’

http://www.itdaily.kr/news/articleView.html?idxno=19499 


외산 DBMS 불만은 ‘높은 유지보수비’   
국산 DBMS 성능 및 지원에 대체적으로 만족
2009년 05월 26일 (화) 22:35:06 김소연 기자soy@itdaily.kr
현재 국내에서 DBMS를 사용 중인 기업의 대부분은 오라클 등 외산 DBMS이다. 이들 외산을 사용하고 있는 고객들은 안정성과 대용량 데이터 처리 능력 등에 대체적으로 만족하고 있는 것으로 조사됐다. 하지만 국산 DBMS에 비해 높은 유지보수액과 서비스 등에는 강한 불만을 갖고 있는 것으로 나타났다.
컴퓨터월드는 최근 공공, 금융, 병원, 대학 등의 산업별 IT 담당자들을 대상으로 ‘DBMS 만족도’를 설문조사한 결과 이 같이 드러났다.

오라클이 89% 점유

설문조사에 답한 기업들이 현재 운영 중인 DBMS 브랜드는 오라클(89%), 마이크로소프트(25%), 사이베이스(16%), 알티베이스(12%), IBM(8%) 순으로 나타났다.

설문에 답한 기업의 79% 이상이 3년 전에 DBMS를 도입했다고 답했으며, 1년 이내와 2년~3년 이내에 도입했다고 밝힌 기업은 각각 10%와 11%였다.

오라클 DBMS를 도입해 사용 중이라는 한 기업의 담당자는 "여러 분야에서 다양하게 사용하는 DBMS라 안정성이 보장되었다는 생각으로 오라클 DBMS를 도입하게 되었다"고 말했다. 오라클 DBMS가 국내 시장에서 보편화되어 비교적 안정된 시스템으로 인식되고 있다는 게 그의 설명이다.

DBMS를 도입해 사용 중인 기업의 대부분은 구축 시스템의 특성에 따라 기술지원, 제품의 안정성, 경제성 등을 복합적으로 검토해 DBMS를 중복 도입해 사용 중인 것으로 나타났다. 그 예로 한 공공기관은 주요 업무 추진에는 오라클 DBMS를, BPM 시스템 구축에는 마이크로소프트 DBMS를 도입해 사용 중이라고 밝혔다. 이 관계자는 "업무에 맞는 DBMS를 구축해 처리의 안정성이 높아 만족하며 사용하고 있다"고 말했다.

한 공공기관 역시 오라클 DBMS는 기간계 시스템에 활용 중이며 시스템 부하가 적은 사이베이스 DBMS는 서비스용 시스템에, 상대적으로 비용이 적게 드는 마이크로소프트 DBMS는 단순 업무 활용에, 알티베이스 DBMS는 메모리 DBMS로 사용하고 있는 것으로 조사됐다.


58%가 DBMS 사용에 만족

설문에 답한 기업의 57.89% 이상이 현재 사용 중인 DBMS에 만족하고 있고, 이어 매우 만족은 11%, 보통은 26%, 불만족스럽다는 5%로 각각 조사됐다.

만족의 이유로는 사용하는데 큰 문제가 없다는 점, 특별한 장애와 제약사항이 없어 업무 처리에 안정적이라는 것이다.

두 종류의 외산 DBMS를 도입해 사용 중이라는 금융권의 한 관계자는 "시장에서 보편적으로 사용하고 있는 DBMS라 애플리케이션이나 여러 솔루션에 대한 호환성이 좋으며 운영에 필요한 정보 공유가 용이하다"며 "특히 유닉스 시스템에서의 뛰어난 안정성을 보장해 주며 대용량 데이터 처리 능력이 강력하다"고 밝혔다.

제품의 기능에 대한 만족도를 묻는 질문에는 74% 이상이 만족하고 있고, 16%는 보통, 10%는 매우 만족한다고 답변했다.

오라클 DBMS를 도입해 사용 중인 한 대학의 관계자는 "오라클 DBMS는 타 제품이 가지지 않는 기능들을 포함하고 있어 전반적으로 만족하고 있다"며 "특히 다양한 함수 기능이 지원되고 사용하기가 용이하다는 것이 만족스럽다"고 말했다.

하지만 그는 "운영 중인 데이터의 규모에 따라 대용량 성능이 반드시 필요하다고는 볼 수 없다"고 말했다.

역시 오라클 DBMS를 사용하고 있다는 한 공공기관의 관계자는 전반적인 만족도가 불만족스럽다고 답변했다. 그 이유를 묻는 질문에 "범용성 및 많은 개발자, 안정적인 성능 보장은 대체로 만족스럽다고 할 수 있지만 버그 및 오류에 대한 지원 및 패치 신뢰도가 낮기 때문이다"고 밝혔다.

반면, 국산 DBMS 제품인 알티베이스 DBMS를 도입해 사용 중인 한 대학의 담당자는 "수강 신청에 대비해 고효율 메모리 DBMS를 도입하고자 했다"며 "국내 통신 시장 등에서 다수의 사이트를 확보하며 안정성이 입증된 알티베이스 DBMS를 도입하게 되었다"고 밝혔다. 덧붙여 그는 "외산 DBMS에 뒤지지 않는 제품의 기능과 성능에 대해 대체로 만족하고 있으며 특히 빠른 처리 속도와 신속하고 친절한 유지보수가 만족스럽다"고 말했다.

성능에 대한 만족도에 대한 질문에는 만족(58%), 보통(26%), 매우 만족(16%) 순으로 나타나 응답자의 대부분이 사용 중인 DBMS의 기능과 성능에 만족하고 있는 것으로 나타났다.

알티베이스 DBMS를 도입해 사용 중인 한 기업의 담당자는 "호환성이 미흡하긴 하지만 빠른 유지보수 대응으로 신속한 처리가 가능하다"며 "특히 대용량 DBMS 사용에도 손색이 없어 국산 DBMS도 성능이 크게 향상됐다"고 밝혔다. 덧붙여 그는 "다양한 제품과의 호환성 완성이 시급한 과제라 할 수 있다"고 말했다.

<상세 내용은 컴퓨터월드 6월 호 참조>

2009년 5월 26일 화요일

DATABASE BOOK 간단리뷰

밥먹기 전에 ... 가지고 있는 DB 책 에 대해서 평을 해본다 .. ㅡㅡ;;

순서는 난이도

1. An Introduction to DATABASE SYSTEMS (C.J.DATE)
- 말할것도 없이 그 유명하신 수학자 DATE 의 책이다. DATE가 수학자여서 그런지 충실히 개념 위주의 설명으로 되어 있으며, 대학 학부 책으로도 꽤 쓰인다. ( 개인적으로 이책이 학부 DB책에는 적합하지 않다고 생각함 )
내용이 다소 주관적이고 증명하기 위해 말이 다소 어렵지만 처음 DB를 접하는 사람들에게 개념을 빡시게(?) 심어주는 좋은책 이라고 할수 있다..( 다만 해석본은 해석이 난해 하다 ..) - (인하대 학부 DB책)

2. DATABASE Principles, Programming, and Performance  (Patrick O’neil / Elizabeth O’neil)
- 외대에서 쓰는 학부DB 책이다. 개인적으로 잘 되어 있는 책이라고 본다. 난이도는 KORTH 책보다는 조금 낮은정도? 개념과 원리를 적절하게 설명한 책이라고 볼수 있겠다.

3. DATABASE SYSTEMS (KORTH) 
- 이것 또한 유명하신 KORTH 가 지은 책이다. 이책의 난이도는 DATE 책 보다 높다. 개념과 원리 를 적절히
  섞어 가면서 기술하였기때문에 DBMS 접근도 일부 들어 있다. DB의 개념을 알고 있다면 이책을 보는것도  
  좋을 거라고 본다. 하지만 난이도 탓인지 챕터마다 자세한 설명은 나와 있지 않다.

4. DATABASE Management Systems (Raghu Ramakrishnan)
- 책이름에서도 나와 있듯이 DBMS 접근방법의 책이라고 볼수 있다. 그래서 위에 책보다 난이도가 꽤 높다.
인덱스나 트리 조인 파일구조 개념및 원리가 꽤 자세하게 기술되어 있다. 난이도가 있기때문에 선행 학습이 필요한 책이라고 볼수 있겠다. ( 성대에서 수업하는 학부 DB책이기도 하다. 학부 DB책으로써는 상당한 난이도를 자랑한다. ;;)

5. DATABASE SYSTEMS THE COMPLETE BOOK (Ullman)
- 유명하신 울만 이 지은 DBMS 책이다. 난이도는 가지고 있는 DB책중에 가장 높다. 철저히 DBMS 구현 중심의 이론들로만 쓰여진 책이다. 이것또한 개념없이 보았다가는 KO 당할수 있다.

오라클 책

1. BOP ( beginning oracle programming )
- 오라클 가장 기본서인 빨갱이 책이다. 내용은 다소 쉽지만 어려운 부분도 일부 존재한다.
오라클에 기초적인 부분들을 두루 설명하고 있으므로 초보자라면 한번 볼만한 책이다. ( 근데 두껍다 ;;)

2. expert one-on-one Oracle 
- BOP를 다 보았다면 이책을 보는것을 권장한다. 비슷한 개념이지만 BOP 보다는 상세하게 오라클 아키텍쳐를 다루고 있다. 개념과 실습 위주의 책으로써 다소 아키텍쳐 부분이 미약한것은 단점이다. ( BOP 보다 더 두껍다 ;;)

3. Expert Oracle Database Architecture 
- 오라클 아키텍쳐에 근거하여 기술한책이다. 오라클 아키텍쳐에 대해서 자세히 알고 있으면 이책은 꼭 봐야 할거 같다. 하지만 기본지식이 없다면 매우 어려울수도 있다.

4. Cost Based oracle fundamentals 
- 난이도 최상이다. CBO 에 대해서 다루고 있으며, QP 하는 사람이라면 꼭 봐야 할 책중 하나다. 개념 숙지 안하고 봤다간 좌절감에 그날 공부는 끝이다 ...

5. ORACLE 공식 교재
- 내용면에서는 이 공식교재를 따라갈 책은 없다. 그만큼 오라클에서 직접 만들었기때문에 매우 좋은 책이다.
하지만 교재값이 엄청비싸고(OCP 취득시에 책값 30~40만원 물론 어둠의 경로가 ..;;) 양이 매우 많다는거..?
이것만 다 숙지해도 오라클의 전체적인 흐름은 알고 있다고 말할수 있다.


등등 잡다한 DB 책은 많지만 .. 저 것들만 다 봐도 2년 농사 성공이다 ..ㅡㅡ;;