이 페이지는 기계 번역되었습니다. 영어 원본을 읽어보세요. English

IBSurgeon 라이브러리

데이터베이스 물리적 구조 (InterBase 및 Firebird)

Alexey Kovyazin, Sergey Vostrikov, last update 05-June-2004

데이터베이스 물리적 구조

왜 InterBase 데이터베이스 물리적 구조를 연구해야 하는가?

일반적으로 InterBase 데이터베이스 물리적 구조를 말할 때, 우리는 데이터가 낮은 수준의 데이터 구성 관점에서 표현된다는 것을 의미합니다 - 바이트 수준까지 말이죠. 고급 언어를 사용하여 애플리케이션을 개발하는 많은 프로그래머들은 낮은 수준의 세부 사항을 연구하는 것을 소홀히 합니다. 그러나 데이터베이스 내부의 데이터 구성 원리를 아는 것은 데이터베이스 애플리케이션의 효과적인 설계의 핵심입니다. 따라서 우리는 InterBase 데이터베이스 구성의 내부로 여행을 떠나 그것이 어떻게 배열되어 있는지 알아볼 것입니다.

그렇다면, 데이터베이스 관리 시스템(DBMS)은 무엇을 위해 사용되는 것일까요? 분명히 데이터를 저장하고 제어하기 위해서입니다. 진부하게 들리지만, 생각해 볼 가치가 있습니다. 사용자는 데이터를 DBMS에 넣고, DBMS는 어떤 방식으로든 이 데이터를 이해할 수 있는 내부 형식으로 변환합니다. “내부 데이터 형식"이라는 단어가 연상에 어려움을 준다면 “0과 1"을 상상할 수 있습니다. DBMS는 이 데이터를 저장하고, 첫 번째 요청 시점에 자체 형식에서 데이터를 추출하여 적절한 형태로 변환한 후 사용자에게 제공해야 합니다.

이 장의 주제는 DBMS가 데이터를 어떻게 저장하는지, 어떤 형태로, 그리고 가장 낮은 수준에서 어떻게 구성되는지입니다. 우리는 HDD에 놓여 있는 비트와 바이트에서 어떻게 가치 있는 데이터를 얻는지 설명하려고 노력할 것입니다.

InterBase 데이터베이스 파일

일반적으로 데이터베이스를 말할 때 우리는 DBMS 자체와 사용자 정보, 그리고 데이터로 작업하는 클라이언트 프로그램까지 의미합니다. 이 장에서는 데이터베이스를 데이터베이스 파일로 간주할 것입니다.

InterBase 데이터베이스는 이 데이터베이스와 관련된 모든 정보를 포함하는 하나 또는 여러 개의 파일을 나타냅니다. 사용자 정보는 예외입니다. 사용자는 전체 서버 수준에서 정의되며 보안 데이터베이스 admin.ib에 별도로 저장되기 때문입니다(7 이전 버전에서는 ISC4.GDB였습니다).

조언: InterBase 보안 원칙에 대해 더 자세히 알아보려면 “서버 및 데이터베이스 보안” 장을 참조하십시오.

따라서 데이터베이스에 대한 모든 정보는 이러한 파일 내에 저장됩니다: 데이터 자체, 인덱스, 트리거, 저장 프로시저 등.

평균적인 프로젝트의 InterBase 데이터베이스는 하나의 파일을 나타냅니다. 최신 InterBase 버전은 데이터 파일 작업에 64비트 I/O를 사용할 수 있어 최대 64GB의 데이터 파일을 가질 수 있기 때문입니다. 이전 버전의 InterBase는 각 데이터베이스 파일당 4기가바이트(전체 데이터베이스의 경우 최대 64테라바이트)의 제약이 있었습니다. 우리가 추측할 수 있듯이, 64기가바이트는 거의 모든 데이터베이스 애플리케이션의 정보를 저장하기에 충분합니다. 그러나 필요한 경우 데이터베이스를 여러 파일로 나눌 수 있습니다. 그런데 수백 기가바이트 크기의 InterBase 데이터베이스도 있습니다.

IBSurgeon - InterBase 데이터베이스를 통한 안내

우리는 InterBase 데이터베이스 파일의 구조를 자세히 알아야 합니다. 따라서 InterBase 서버 커널을 통하지 않고 데이터베이스 파일로 직접 작업할 수 있는 편리한 도구가 있는 것이 바람직합니다. 가장 쉬운 방법은 일반 16진수 뷰어를 사용하여 HEX 표현을 고려하여 데이터베이스 파일의 구조를 이해하려고 시도하는 것입니다. 그것은 꽤 지루한 작업이 될 것입니다.

하지만 다행히도 InterBase 데이터베이스로 직접 작업할 수 있는 도구가 있습니다. 그것은 IBSurgeon Editor입니다 - InterBase 데이터베이스와 직접적인 낮은 수준의 작업을 위한 도구로, InterBase 데이터베이스의 내부 구조를 연구하고 복원을 위해 손상된 데이터베이스를 진단하는 데 사용할 수 있습니다. 자세한 내용은 부록 “관리자 및 InterBase 개발자 도구"를 참조하십시오.

IBSurgeon은 자체적인 대체 데이터베이스 액세스 메커니즘을 사용하여 InterBase/FireBird/Yaffil 서버 커널로는 열 수 없는 심하게 손상된 상태를 포함한 모든 상태의 데이터베이스를 열고 검토할 수 있습니다.

우리는 IBSurgeon을 사용하여 데이터베이스의 내부 구조를 설명할 것입니다.

내부에서 본 *.IB/*.FDB 파일

IB는 InterBase 데이터베이스 파일에 권장되는 확장자이고, FDB는 Firebird용입니다(이전에는 GDB였습니다). IB 파일의 구조에 대해 가장 먼저 말해야 할 것은 엄격하게 정의된 크기의 페이지 집합을 나타낸다는 것입니다. 데이터베이스 파일의 크기는 페이지 크기로 나눌 수 있으며, 이 페이지 크기는 이 데이터베이스의 모든 파일에 대해 변경되지 않습니다. 다른 InterBase 버전은 다른 페이지 크기를 지원하며, 이는 표 1에 나와 있습니다. 페이지 크기는 데이터베이스 생성 시 설정되며 수명 주기 동안 변경할 수 없습니다. 즉, 백업에서 데이터베이스를 복원할 때만 페이지 크기를 변경할 수 있습니다.

표 1. 다른 InterBase 버전에서 지원하는 페이지 크기

InterBase 버전 페이지 크기, 바이트
1024 2048 4096 8192 16384
InterBase 4.0 * * * *
InterBase 5.x * * * *
InterBase 6.x-7.x * * * *

데이터베이스의 데이터 읽기 및 쓰기는 페이지 단위로 실행되며, 데이터베이스 캐시 크기와 같은 많은 중요한 서버 및 데이터베이스 특성은 페이지 크기에 따라 달라지며 “페이지” 단위로 계산됩니다.

IBSurgeon으로 InterBase 데이터베이스를 열어 봅시다. 데이터베이스 파일을 두 번 클릭하는 것으로 충분합니다. 그림 1은 IBSurgeon이 데이터베이스를 연 후 나타나는 페이지 목록을 나타냅니다:

![](/images/IB DB structure/InterBase database structure_html_m4f802417.png)

그림 1. 데이터베이스 페이지 목록

페이지는 다양한 유형이 될 수 있으며, 각 유형은 특정 목적을 제공합니다. 다른 유형 간의 상호 의존성은 그림 2에 조건부로 표현되어 있습니다. 그림 2는 파일 시작부터 계산할 때 왼쪽에서 오른쪽으로, 위에서 아래로 데이터베이스 파일의 페이지 할당을 개략적으로 묘사합니다. 같은 유형의 페이지는 엄격하게 연속적으로 가지 않습니다 - 서버가 데이터베이스를 확장하거나 생성할 때 생성된 순서대로 파일에 쉽게 혼합되어 할당될 수 있습니다.

![](/images/IB DB structure/InterBase database structure_html_4005a6b0.png)

그림 2. InterBase 데이터베이스의 다른 유형의 페이지 간 상호 의존성

일부 페이지 유형은 다른 유형의 페이지에 대한 참조가 없다는 것을 눈치챘을 것입니다. 그러나 여기에는 모순이 없습니다. 문제는 이러한 유형의 페이지가 다른 구조적 수준에서 연결되고 사용된다는 것입니다. 그것들은 RDB$PAGES 테이블 및 기타 시스템 테이블에 연결될 수 있습니다(이 테이블 및 기타 시스템 개체는 아래 “데이터베이스 논리적 구조” 장에서 고려할 것입니다). 그림 2에서는 물리적 수준에서 페이지 간의 명시적 참조만 볼 수 있습니다.

InterBase 데이터베이스에 어떤 유형의 페이지가 있는지 자세히 고려해 봅시다. InterBase 기본 코드 세트의 ods.h 파일에는 가능한 모든 페이지 유형에 대한 정보가 있습니다. 우리는 ODS뿐만 아니라 InterBase 커널의 다른 많은 기본적인 사항에 대한 데이터를 원본 소스에서 얻기 위해 이 파일을 자주 참조할 것입니다. 총 11가지 유형의 페이지가 선언되어 있지만 그 중 9개만 설명할 가치가 있습니다(표 2에서 명확히 볼 수 있습니다). 식별자가 0과 1인 페이지 유형은 정의되지 않았거나 사용되지 않습니다.

표 3. FB의 페이지 유형

ods.h의 정의 페이지 유형 식별자 페이지 설명
pag_undefined 0 정의되지 않음 - 페이지에 이 페이지 유형이 있으면 아마도 사용 가능한 것입니다
pag_header 1 데이터베이스 헤더 페이지
pag_pages 2 페이지 인벤토리 페이지(또는 공간 인벤토리 페이지 - SIP)
pag_transactions 3 트랜잭션 인벤토리 페이지(TIP)
pag_pointer 4 포인터 페이지
pag_data 5 데이터 페이지
pag_root 6 인덱스 루트 페이지
pag_index 7 인덱스(B-트리) 페이지
pag_blob 8 Blob 데이터 페이지
pag_ids 9 Gen-ids
pag_log 10 쓰기 전 로그 정보

모든 페이지에는 페이지 유형과 동일한 유형의 다음 페이지 번호에 대한 정보를 포함하는 헤더가 있습니다. 정의 파일 ods.h에서 pag 구조를 고려하면 모든 페이지 헤더가 포함하는 매개변수의 전체 목록을 얻을 수 있습니다.

/* 기본 페이지 헤더 */

typedef struct pag {

SCHAR pag_type; /*페이지 유형 식별자*/

SCHAR pag_flags; /*페이지 플래그*/

USHORT pag_checksum; /*페이지 체크섬: 5.0 버전 이후에는 12345와 같음*/

ULONG pag_generation; /*페이지 세대*/

ULONG pag_seqno; /* 마지막 업데이트의 WAL 시퀀스 번호 - 더 이상 사용되지 않음*/

ULONG pag_offset; /* 마지막 업데이트의 WAL 오프셋 - 더 이상 사용되지 않음*/

} *PAG;

페이지 유형 및 그 사용

각 페이지 유형을 자세히 살펴보고 그 기능과 포함된 정보에 대해 알아보겠습니다. 첫 페이지부터 단계별로 시작하겠습니다.

데이터베이스에 대한 모든 작업은 데이터베이스 헤더 페이지(또는 헤더 페이지)를 읽는 것에서 시작됩니다. 데이터베이스 헤더 페이지는 모든 데이터베이스 파일에서 첫 번째로 옵니다. 따라서 그림 2에서 첫 번째로 표시됩니다(그림이 데이터베이스 파일의 확장을 왼쪽에서 오른쪽으로, 위에서 아래로 나타낸다고 가정할 경우).

헤더 페이지는 데이터베이스 전체에 대한 정보를 포함합니다. 그림 3에서 데이터 페이지는 IBSurgeon이 우리에게 보여주는 방식으로 표현됩니다:

![](/images/IB DB structure/InterBase database structure_html_m3c792928.png)

그림 3. 데이터베이스 헤더 페이지.

데이터베이스 통계를 얻으면 헤더 페이지 내용에 대한 아이디어를 얻을 수 있습니다. 이를 위해 명령줄 유틸리티 gstat 또는 «관리자 및 InterBase 개발자 도구» 애플리케이션 목록에 있는 더 편리한 InterBase 관리 도구를 사용할 수 있습니다. 통계 수신 과정 및 헤더 페이지 데이터 설명에 대한 자세한 내용은 «통계» 장을 참조하십시오.

헤더 페이지에는 페이지 크기, ODS 버전 번호(이에 대한 정보는 아래에서 찾을 수 있음), 데이터베이스 생성 날짜, 트랜잭션 정보 및 다양한 정보 세트와 같은 중요한 정보가 포함되어 있다는 점에 유의해야 합니다. 예를 들어, 구현 ID는 이 데이터베이스가 어떤 운영 체제에서 생성되었는지에 대한 정보를 저장합니다.

데이터베이스에 연결할 때 InterBase 서버는 파일 시작 부분에서 처음 1024바이트의 정보를 읽고 읽은 값에 따라 연결 줄에 지정된 파일이 InterBase 데이터베이스인지 여부를 정의합니다. 그런 다음 서버는 헤더 페이지에서 ODS 버전 번호와 이 데이터베이스의 페이지 크기를 읽고, ODS 버전이 서버 구현과 호환되면 처음 1024바이트에서 받은 적절한 페이지 크기를 사용하여 전체 헤더 페이지를 다시 읽습니다. 그 후 읽기-쓰기 모드, 데이터베이스 방언 등과 같은 나머지 중요한 데이터베이스 매개변수가 헤더 페이지에서 읽힙니다.

헤더 페이지에는 메타데이터를 포함하는 데이터 페이지에 대한 참조를 저장하는 첫 번째 포인터 페이지에 대한 참조가 있습니다: RDB$Pages 테이블(아래 «InterBase 데이터베이스 논리 구조» 장 참조). 그림 2에서 이 참조는 «데이터베이스의 첫 번째 포인터 페이지 번호»라는 문구가 있는 화살표로 설명됩니다. 서버는 헤더 페이지에서 첫 번째 포인터 페이지의 번호를 읽고 그 페이지로 이동합니다. 포인터 페이지는 특정 테이블을 구성하는 데이터 페이지 번호의 정렬된 배열로 구성됩니다(테이블은 데이터베이스 논리 구조로 설명되는 SQL 객체로 간주됨). 이제 IBSurgeon이 포인터 페이지를 어떻게 해석하는지 볼 수 있습니다(그림 4 참조):

![](/images/IB DB structure/InterBase database structure_html_3bb85660.png)

그림 4. InterBase 데이터베이스 포인터 페이지

페이지에는 데이터 페이지 벡터가 포함되어 있습니다; 이 데이터는 데이터베이스의 특정 테이블을 구성합니다. 이 벡터는 파일의 데이터 페이지 번호에 해당하는 포인터 배열을 나타냅니다. 서버는 데이터 페이지의 4바이트 번호를 읽고 필요한 데이터 페이지로 이동합니다. RDB$Pages의 첫 번째 데이터 페이지로 이동하면 서버는 이후 모든 데이터베이스 작업에 사용되는 내부 데이터베이스 표현 구성을 시작합니다. RDB$Pages는 데이터베이스에 대한 정보를 포함하는 데이터 페이지뿐만 아니라 데이터베이스 작업을 제공하는 데 역할을 하는 나머지 페이지에 대한 참조도 저장합니다.

우리는 이 테이블을 자주 언급하는데, 엄밀히 말하면 데이터베이스 논리 구조에 속합니다. 그럼에도 불구하고 모든 것이 서로 연결되어 있으므로 다른 것을 언급하지 않고 무언가를 설명할 수 없습니다.

중요한 페이지 유형 중 하나는 트랜잭션 인벤토리 페이지(TIP)입니다. 이러한 페이지는 다른 모든 페이지와 마찬가지로 헤더와 2바이트 시퀀스 배열을 나타내는 주요 부분으로 구성됩니다. 시퀀스는 데이터베이스의 트랜잭션 상태를 설명합니다(트랜잭션에 대한 자세한 내용은 «트랜잭션» 장 참조).

표 4. TIP에서 가능한 트랜잭션 상태

PIP의 시퀀스 값 의미
0 트랜잭션이 시작되지 않았거나, 활성 상태이거나, 커밋 또는 롤백 없이 손실됨
1 트랜잭션이 커밋 실행
2 트랜잭션이 롤백 실행
3 임보 트랜잭션(2PC용)

모든 레코드 버전에는 트랜잭션 식별자가 있어 동시에 실행되는 트랜잭션이 서로의 상태를 «알고» 다중 사용자 작업 중 충돌을 해결할 수 있습니다(레코드 버전 및 기타 사항에 대한 자세한 내용은 «InterBase의 다중 세대 아키텍처» 장 참조).

데이터베이스 헤더 페이지, 포인터 페이지 및 TIP는 서버에서만 사용되는 «하우스키핑» 페이지 유형에 속합니다. InterBase 사용자는 그들이 포함하는 정보를 명시적으로 얻지 않습니다. 페이지 할당에 대한 정보를 저장하는 페이지(일반적으로 페이지 인벤토리 페이지(PIP) 또는 공간 인벤토리 페이지(SIP)로 언급됨)도 하우스키핑 페이지 유형에 속합니다. 이러한 페이지는 두 번째부터 시작하여, 즉 첫 번째 PIP가 헤더 페이지 바로 뒤에 오며, 다른 유형의 고정 페이지 간격으로 데이터베이스에 나타납니다. 이 간격의 크기는 PIP가 나타나는 다른 유형의 페이지 수를 의미하며, 이 데이터베이스에 설정된 페이지 크기에 따라 다릅니다. 페이지 인벤토리 페이지는 포인터 페이지에 계산되지 않으며 RDB$Pages에 표시되지 않습니다. 이러한 페이지의 무결성은 전체 데이터베이스의 성공적인 작업에 필수적입니다. PIP 내용이 데이터베이스의 나머지 모든 페이지 상태를 설명하기 때문입니다. 모든 데이터베이스 페이지는 3가지 상태를 가질 수 있습니다: 할당되지 않음, 공간으로 할당됨, 할당 및 가득 참. 새 데이터를 위한 추가 공간이 필요할 때 서버는 할당되지 않은 페이지가 있는지 PIP를 확인합니다. 그러한 페이지가 있으면 서버는 그 상태를 공간으로 할당됨으로 수정합니다. 할당되지 않은 페이지가 없으면 데이터베이스가 확장됩니다 - 새 데이터 페이지가 추가됩니다.

IBSurgeon의 데이터 페이지 예와 포함된 데이터는 그림 5에 나와 있습니다.

![](/images/IB DB structure/InterBase database structure_html_1cb26c4f.png)

그림 5. 페이지 인벤토리 페이지

페이지가 할당되자마자 InterBase는 SIP에 상태를 기록한 다음 페이지 자체를 기록합니다. 그 후, 새로 형성된 이 페이지를 많은 수의 페이지, 예를 들어 테이블의 데이터 페이지에 추가해야 합니다. 이를 위해 이 많은 수의 페이지의 마지막 페이지, 예를 들어 테이블의 마지막 데이터 페이지에 이 새 페이지에 대한 참조를 작성해야 합니다. 서버가 SIP에 기록한 직후 작업을 중단했지만 방금 할당된 페이지를 참조하는 페이지에 참조를 작성하지 않았다면 이 페이지는 고아가 됩니다. 고아 페이지는 물리적으로 생성되고 SIP에 예약되지만 다른 페이지에서 이에 대한 참조가 없으므로 서버가 이를 찾아 디스크에 데이터를 쓰지 못합니다. 고아 페이지는 그림 2에서 빨간 사각형으로 표시됩니다. 고아 페이지는 주로 서버의 예기치 않은 전원 차단으로 인해 발생하며 데이터베이스 복구를 위한 특수 도구 gfix (또는 FirstAID) (또는 IBSurFirstAID)로 «치료»됩니다.

데이터 페이지를 고려하기 전에 중요한 페이지 유형인 생성기인덱스 _페이지_에 대해 언급해야 합니다. 생성기 페이지는 생성기 상태를 보여주는 4바이트 숫자 배열을 나타냅니다. 실제로 생성기는 일반 카운터입니다.

그림 6에서 생성기 페이지를 볼 수 있습니다. IBSurgeon이 생성기 이름을 표시하지만 이러한 이름이 생성기 페이지에 저장되는 것은 아니라는 점에 유의하십시오. 이는 데이터베이스를 연구하는 사용자의 편의를 위해 만들어진 것입니다. 실제로 생성기 이름은 시스템 테이블 RDB$Generators에 저장됩니다.

![](/images/IB DB structure/InterBase database structure_html_7728f31.png)

그림 6. 생성기 페이지 (g en-ids)

이 예제에서 볼 수 있듯이 데이터베이스에는 RDB$ 접두사로 시작하는 시스템 생성기와 사용자 정의 생성기가 포함되어 있습니다. InterBase 데이터베이스 애플리케이션 개발 시 생성기의 기능과 사용법에 대해 알아보려면 «테이블. 기본 키 및 생성기» 장을 참조하십시오. 생성기 페이지는 RDB$Pages 테이블의 다른 페이지와 함께 계산됩니다.

모든 테이블에는 인덱스가 있는지 여부와 관계없이 최소한 하나의 인덱스 루트 페이지가 있습니다. 이 페이지에는 해당 테이블의 인덱스 페이지에 대한 포인터가 포함되어 있습니다. 인덱스 루트 페이지는 데이터 페이지에 대한 포인터 페이지와 마찬가지로 인덱스 페이지에 대해 동일한 중요성을 가진다고 말할 수 있습니다. 따라서 IBSurgeon은 이를 유사한 방식으로 표시합니다. 인덱스 루트 페이지의 예는 그림 7에 나와 있습니다.

![](/images/IB DB structure/InterBase database structure_html_1c46b1cd.png)

그림 7. 인덱스 루트 페이지

인덱스 루트 페이지에는 인덱스 값이 저장된 페이지 목록과 인덱스 정보(인덱스 선택도 및 다양한 플래그)가 포함됩니다. InterBase 데이터베이스에서 인덱스의 역할과 사용에 대한 자세한 내용은 «인덱스» 장을 참조하십시오.

인덱스 페이지에는 인덱스 값이 직접 저장되거나, 인덱스 레벨이 0보다 큰 경우 하위 인덱스 페이지에 대한 참조가 저장됩니다. 인덱스 페이지의 예는 다음과 같습니다(그림 8).

![](/images/IB DB structure/InterBase database structure_html_m35b3f5ff.png)

그림 8. 인덱스(B-트리) 페이지

인덱스 페이지에는 인덱스된 데이터의 압축된 값이 저장됩니다. 특히 복합 인덱스(여러 필드 포함)를 만들 때 상당히 복잡한 인덱싱 메커니즘이 사용됩니다.

일반적으로 데이터 페이지와 BLOB 값을 포함하는 페이지는 사용자 정보를 저장합니다. 데이터 페이지에는 데이터베이스의 사용자 테이블에 있는 레코드, 레코드 조각, 이전 버전, 버전 간 차이, BLOB 필드 등이 포함됩니다. BLOB 필드의 경우 데이터 페이지의 레코드와 연결되어 있으며 데이터 페이지에 위치할 수 없는 대용량 데이터를 포함합니다. BLOB 값을 참조 방식으로 저장하면 대용량 데이터를 저장할 수 있습니다.

IBSurgeon에서 데이터 페이지 표시의 예는 그림 9에 나와 있습니다:

![](/images/IB DB structure/InterBase database structure_html_235e0c51.png)

그림 9. 데이터 페이지

데이터 페이지 헤더에는 페이지 유형, 소유 테이블 식별자(relationID)가 포함됩니다. 레코드는 페이지 끝에서부터 데이터 페이지에 저장되며, 채워짐에 따라 페이지 시작 부분에 가깝게 할당됩니다.

행 인덱스(페이지 내 오프셋과 길이의 두 값을 포함)를 살펴보면 이를 확인할 수 있습니다. 보시다시피 행의 시작 부분에는 페이지 끝에 할당된 레코드가 있습니다. 예를 들어 첫 번째 레코드의 오프셋은 8156바이트이고 길이는 34바이트이므로 8156+34=8192바이트에서 끝나며, 페이지의 맨 가장자리(이 경우 페이지 크기는 8192바이트)에 위치합니다. 페이지가 채워지면(위에서 데이터, 아래에서 레코드 인덱스) 서버는 새 페이지에 새 레코드와 이전 레코드의 버전을 쓰기 시작합니다. 위에서 설명한 페이지 채움 메커니즘을 통해 InterBase 전문가들이 대용량 데이터 페이지(최소 4096바이트, 가능하면 8192바이트)를 강력히 권장하는 이유를 쉽게 알 수 있습니다. 한 레코드가 상당히 큰 크기(예: VARCHAR(255) 필드 10개)가 될 테이블을 만들면 2550바이트 이상을 차지하고 채워질 것입니다. 즉, 이러한 레코드는 작은 크기의 페이지(1024 또는 2048)에는 너무 큽니다. 단일 레코드를 읽기 위해 디스크에서 여러 페이지를 로드해야 하는 것은 데이터베이스 작업 속도를 높이지 않을 것이 분명합니다. 따라서 기본적으로 1024바이트 크기가 설정되어 있으므로 데이터베이스를 생성하거나 복원할 때 데이터 페이지 크기를 재정의하는 것이 좋습니다. 방금 InterBase 데이터 파일 페이지의 주요 유형과 그 기능을 간략히 살펴보았습니다. 이제 더 높은 구조적 수준으로 넘어갈 수 있습니다.

ODS

ODS는 On-Disk Structure의 약어로, 디스크에 있는 InterBase 데이터베이스의 데이터 구조를 의미합니다. ODS는 데이터베이스 파일 내 데이터가 구성되는 방식을 정의합니다. On-Disk 구조 구현을 위한 주요 상수와 데이터 구조의 정의는 InterBase 소스 코드 세트의 ods.h 파일에 있습니다. ODS는 InterBase 개발 과정에서 변경되었으며, 특정 데이터베이스로 작업할 때 서버는 ODS 버전 번호를 확인하여 무엇을 다루고 있는지 파악합니다. ods.h 파일은 다음과 같은 On-Disk 구조 버전을 보여줍니다:

  • ODS 5는 InterBase 3.3에서 사용되었으며 그 이상 버전에서는 지원되지 않습니다

  • ODS 6 및 ODS 7은 출시되지 않았습니다

  • ODS 8은 InterBase 4.0에서 사용됩니다

  • ODS 9는 InterBase 4.5 이상에서 사용됩니다

  • ODS 10은 InterBase 6과 함께 출시되었습니다

  • ODS 11은 InterBase 7.0과 함께 출시되었습니다

주요 ODS 버전 외에도 이를 생성한 데이터베이스 서버의 특정 버전에 따라 달라지는 부 버전이 있습니다. 버전의 주요 번호는 숫자의 정수 부분에 기록되고 부 번호는 소수 부분에 기록됩니다. 예를 들어 서버 버전 4.0은 ODS 8.0을 가진 데이터베이스를 생성하고 InterBase 4.2는 ODS 8.2를 생성합니다. 부 버전 간의 하위에서 상위로의 전환은 자동으로 수행됩니다. 예를 들어 서버 4.0이 생성한 ODS 8.0 데이터베이스를 InterBase 5.6으로 열기만 하면 이 데이터베이스의 ODS는 버전 8.2가 됩니다. 주요 데이터베이스 버전 간의 전환은 이전 버전을 사용한 데이터베이스 백업과 새 서버 버전을 사용한 복원을 통해서만 수행됩니다. 버전 간 전환 과정은 1.4장 «마이그레이션»에서 자세히 설명합니다.

InterBase 4.x 및 5.x 버전에 대한 ODS 지원 구현에서 중요한 점은 InterBase 서버 4.x 및 5.x가 특정 서버 구현보다 한 단계 낮은 버전과의 하위 호환성입니다. InterBase는 여러 가능한 ODS를 지원하며, 특정 데이터베이스에 연결할 때 해당 ODS 버전에 따라 필요한 ODS 구현 지원을 선택합니다. 특정 경우에 어떤 ODS 지원 구현을 선택할지 결정하는 메커니즘을 Y-Valve(Steve Trenton의 저작)라고 합니다.

쉽게 말하면, InterBase 4.0에 해당하는 ODS 8.x 데이터베이스는 InterBase 5.x에서 열 수 있습니다.

전체 ODS 호환성 표는 아래와 같습니다:

InterBase 버전 주요 ODS 부 ODS
4.0/4.1 8.0
4.2 8.2 8.2
5.0/5.1 9.0 8.2
5.5 9.1 8.2
5.6 9.1 8.2
6.0 10.0 9.0/9.1
7.0 11.0 10.0
7.1 11.1 10.0

ODS는 하위 호환성을 가집니다. 즉, 더 높은 버전의 서버와 그 모든 도구는 이전 버전의 서버가 생성한 데이터베이스로 작업할 수 있지만 그 반대는 불가능합니다. InterBase 5.x로 InterBase 6 버전에서 생성된 데이터베이스를 열려고 하면 «Unsupported On-disk structure: Found ODS 10, supported ODS 9» 오류 메시지를 받게 됩니다.

하위에서 상위로의 버전 전환 및 그 반대에 대한 설명은 «마이그레이션» 장을 참조하십시오.

ODS는 백업 및 데이터베이스 추출, 손상된 데이터베이스 복원과 관련된 문제에 매우 중요합니다. 백업 도구 gbak 및 복원 도구 gfix는 ODS 버전을 확인하며, 서비스해야 할 데이터베이스의 ODS 버전이 도구에 구현된 버전보다 크면 작동하지 않습니다. 즉, 4.x의 gbak은 5.x 서버가 생성한 데이터베이스의 백업을 만들 수 없지만 그 반대는 쉽게 가능합니다.

데이터베이스 물리적 구조와 논리적 구조 사이의 다리

데이터베이스 파일의 물리적 구조를 일반적인 방식으로 살펴보았습니다. 이제 데이터베이스 논리적 구조로 넘어가야 합니다. 개념의 경계와 자료의 공백이 없도록 데이터베이스의 정보 표현에서 물리적 수준과 논리적 수준 사이에 다리를 만들어 보겠습니다. 다양한 데이터베이스 페이지에 저장된 모든 것은 컴퓨터 메모리에서 어떤 방식으로든 구성되어야 합니다. 데이터베이스 파일의 데이터는 일련의 서버 내부 객체와 변수로 변환되어야 합니다. 이 집합을 Ann Harrison의 용어에 따라 내부 데이터베이스 이미지라고 합니다 [1]. 그럼 내부 데이터베이스 이미지를 만드는 과정을 살펴보겠습니다.

  • 서버는 파일의 시작 부분에서 1024바이트를 읽고, 그것이 실제로 InterBase 데이터베이스 파일이라면 이 데이터베이스의 페이지 크기를 결정하고 전체 헤더 페이지를 다시 읽습니다.

  • 헤더에서 페이지 서버는 데이터 페이지에 대한 참조를 저장하는 포인터 페이지의 번호를 추출하여 RDB$Pages 테이블을 정의합니다.

  • 서버는 이 포인터 페이지로 이동하여 가리키는 데이터 페이지에서 정보를 읽기 시작합니다. 첫 번째 RDB$Pages 테이블을 데이터로 채웁니다. 이 테이블은 물리적 객체(데이터베이스 파일의 페이지)와 논리적 객체(테이블) 사이의 다리와 같은 역할을 합니다. RDB$Pages의 구조는 다른 시스템 테이블과 마찬가지로 InterBase에서 엄격하게 고정되어 있습니다.

  • 관계별 페이지 할당에 대한 데이터를 받은 후(관계는 실제로 일반 테이블과 동일하며, 단순화를 위해 이러한 개념을 정신적으로 대체할 수 있음), InterBase는 데이터 구조를 형성하기 시작합니다: 먼저 시스템 테이블, 제약 조건 및 인덱스, 그 다음 사용자 객체입니다.

  • 시스템 및 사용자 메타데이터(테이블, 제약 조건, 인덱스 및 기타 데이터베이스 객체)의 초기화 후, InterBase는 데이터베이스 열기를 요청한 사용자에게 이 데이터베이스의 핸들을 반환합니다. 핸들에는 InterBase가 어떤 데이터베이스로 작업할지 보여주는 식별자가 포함되어 있습니다. 여러 사용자가 동시에 작업할 수 있고 여러 데이터베이스가 열릴 수 있기 때문입니다.

  • 이러한 작업 후 데이터베이스는 열린 것으로 간주되며 서버는 사용자 쿼리를 실행할 준비가 됩니다. 이제 데이터베이스의 물리적 구조와 논리적 구조를 연결하는 특정 다리가 만들어졌으므로 논리적 구조의 특성을 연구하기 시작할 수 있습니다.

InterBase 데이터베이스 논리적 구조

논리적 구조는 다소 모호한 개념이므로, 나중에 직관적으로 명확해지기를 바라며 핵심 아이디어를 점진적으로 익히도록 노력하겠습니다. 먼저 데이터베이스 논리적 구조와 관련하여 고려할 것은 시스템 테이블과 그 내용입니다. 시스템 테이블은 시스템뿐만 아니라 사용자 메타데이터도 설명합니다. 일반적으로 “메타데이터"라는 용어는 “데이터 집합을 설명하는 데이터"를 의미합니다. 접두사 “메타"는 “집합을 설명한다"는 뜻입니다. 예를 들어, 메타 언어는 언어 집합을 설명하는 언어입니다. 메타데이터는 사용자 데이터, 즉 테이블, 트리거, 뷰, 저장 프로시저 등을 설명합니다. 이는 이 특정 데이터베이스가 생성된 이유인 정보 저장 및 처리 규칙을 구현하는 모든 것입니다.

처음에는 모든 메타데이터(사용자 테이블, 트리거, 뷰 및 모든 시스템 객체)가 일반 SQL 쿼리로 데이터를 읽고 쓸 수 있는 동일한 테이블에 저장된다는 사실이 꽤 흥미롭습니다. 이러한 테이블은 이름이 RDB$로 시작한다는 점에서만 “시각적으로” 다릅니다. 이 4개의 기호는 시스템 객체의 이름을 위해 예약되어 있습니다. 어떤 사용자 테이블, 열 또는 다른 객체도 이러한 기호로 시작하는 이름을 가질 권리가 없습니다. 공식적으로 예약된 기호로 시작하는 이름의 테이블을 만들 수 있지만 InterBase 문서에서는 이를 권장하지 않습니다.

질문이 생깁니다: 데이터베이스 구조에 대한 데이터가 사용자 데이터와 동일한 테이블에 저장된다면, 테이블을 설명하는 테이블에 대한 정보는 어디에 저장됩니까? “닭과 달걀” 문제의 전형적인 예입니다 - 서로 의존적이라면 어떻게 하나가 다른 것보다 먼저 나타날 수 있습니까? 답은 시스템 테이블이 원시 상태로 초기 InterBase 코드에 고정되어 있으며 데이터베이스 생성 시 특정 순서로 자동으로 열린다는 것입니다. 우리는 이미 데이터베이스 파일의 물리적 페이지를 이 데이터베이스의 특정 객체와 비교하는 RDB$Pages 테이블에 대해 이야기했습니다. 이 테이블의 구조는 아래와 같습니다:

표 5. 시스템 테이블 RDB$Pages

열 이름 데이터 유형 설명
RDB$PAGE_NUMBER INTEGER 물리적 페이지 번호
RDB$RELATION_ID SMALLINT 페이지가 할당된 테이블의 식별자
RDB$PAGE_SEQUENCE INTEGER 이 페이지의 번호
RDB$PAGE_TYPE SMALLINT 페이지 유형 - 표 3 참조

모든 데이터 페이지는 특정 테이블과 관련되어 있습니다. 이 관계는 테이블에 대한 참조가 저장되는 RDB$RELATION_ID 필드에 의해 유지됩니다. 위에서 설명한 대로, 내부 데이터베이스 이미지를 구성하는 과정에서 서버는 이 테이블을 만들고 설정된 알고리즘에 따라 데이터로 채웁니다. 정확히 말하면, 내부 데이터베이스 이미지를 구성하는 시점에 RDB$Pages는 테이블이 아니라 InterBase에 알려진 특정 형식의 데이터 파일일 뿐입니다. 고정된 알고리즘에 따라 서버는 이 파일에서 데이터를 읽고 전체 데이터베이스에 중요한 테이블인 RDB$Relations를 만듭니다. 이 테이블은 모든 데이터베이스 테이블을 설명합니다. SQL 쿼리를 수행하면:

SELECT * from RDB$Relations

RDB$Relations에 어떤 테이블에 대한 참조가 포함되어 있는지 확인하면 RDB$Pages와 그 자체가 포함되어 있음을 알 수 있습니다. 이 경우 서버가 약간 교묘하게 행동하여 RDB$Relations에 이러한 시스템 테이블과 다른 시스템 테이블을 소급하여 대체하고 그렇게 합법화한다는 것이 분명합니다. 서버는 이를 레코드를 추가하거나 삭제할 수 있는 “일반” 테이블로 기록합니다. 즉, 메타데이터 작업을 위한 표준 SQL 인터페이스를 제공합니다.

그리고 합리적인 질문이 생길 수 있습니다 - 왜 InterBase 개발자는 시스템 데이터를 사용자 인터페이스에 맞게 조정했을까요? 결국 내부 액세스 및 읽기 작업 메커니즘이 더 빠를 것입니다. 물론 메타데이터를 설명하는 테이블 작업을 위한 보편적인 메커니즘을 제공하는 데 큰 의미가 있습니다.

문제는 데이터베이스 논리적 구조가 테이블뿐만 아니라 다른 객체로도 구성된다는 것입니다. InterBase에는 다음과 같은 객체가 있습니다:

  • 테이블

  • 트리거

  • 계산된 필드

  • 검증

  • 프로시저

  • 표현식 인덱스

  • 예외

  • 사용자

  • 필드

  • 인덱스

  • 사용자 정의 함수(UDF)

아직 일부 객체의 기능을 확실히 알지 못하지만, 모든 객체가 사용자와 InterBase 커널의 액세스에 편리한 어떤 형태로 설명되고 저장되어야 한다는 것은 확실합니다. 가장 좋은 방법은 이러한 객체를 시스템 테이블에 저장하는 것입니다. 추가 및 수정은 SQL 쿼리로 수행됩니다. 현명한 해결책이죠? 서버 구현은 특정 데이터베이스와 완전히 분리되어 있습니다 - 모든 상호 연결은 SQL 및 그 확장(저장 프로시저 및 트리거 언어)으로 설명됩니다.

따라서 모든 서버 객체는 테이블에 저장됩니다. 각 객체 유형에는 데이터베이스에 설명된 모든 인스턴스를 설명하는 테이블이 있습니다. 예를 들어, 트리거에는 RDB$Triggers 테이블이 있고, 저장 프로시저에는 RDB$Procedures가 있으며, 뷰는 RDB$Relations 테이블에 설명됩니다.

데이터베이스의 모든 테이블과 뷰를 설명하는 마지막 테이블의 구조를 자세히 살펴보겠습니다. RDB$RELATIONS 테이블의 구조는 InterBase 6용 Language Reference에서 가져온 것으로 아래 표 6에 나와 있습니다.

표 6. 시스템 테이블 RDB$Relations

열 이름 데이터 유형 길이 설명
RDB$VIEW_BLR BLOB 80 BLR: 뷰의 경우, InterBase가 뷰에 적용할 때마다 수행하는 쿼리의 BLR(Binary Language Representation)을 포함합니다.
RDB$VIEW_SOURCE BLOB 80 텍스트: 뷰의 경우, 이 뷰를 구현하는 SQL 쿼리 코드를 포함합니다.
RDB$_DESCRIPTION BLOB 80 테이블 또는 뷰의 사용자 설명
RDB$RELATION_ID SMALLINT 테이블/뷰의 내부 식별자를 포함합니다
RDB$SYSTEM_FLAG SMALLINT 테이블 유형을 정의합니다: 사용자 데이터 - 0; 시스템 정보 > 0.
RDB$DBKEY_LENGTH SMALLINT db$key 길이
RDB$FORMAT SMALLINT InterBase 내부 사용을 위해 예약됨. 주어진 테이블에 대한 메타데이터 수정 카운터를 포함합니다.
RDB$FIELD_ID SMALLINT 테이블의 필드 수.
RDB$RELATION_NAME CHAR 31 고유한 테이블 이름.

이 시스템 테이블의 설명에서 BLR이라는 약어를 볼 수 있습니다. 그것이 무엇인지 이해하기 위해 SQL에 대한 설명을 하겠습니다. 알려진 바와 같이 뷰, 트리거 및 저장 프로시저는 SQL 언어의 확장(각 DBMS 서버마다 고유한 확장이 있음)으로 작성된 코드입니다. 이는 인간 언어에 가까워 쿼리를 쉽게 작성할 수 있습니다. 그러나 InterBase는 분명히 이를 더 “기계적인” 것으로 변환합니다 - 즉 BLR(Binary Language Representation)로 변환합니다. 모든 쿼리, 뷰, 트리거, 저장 프로시저는 항상 BLR로 변환된 다음 실행을 위해 InterBase 커널로 전송됩니다.

BLR

BLR은 프로그래머가 작성하는 SQL 코드와 서버가 인식하는 기계 코드 사이의 중간 연결 고리로 사용되는 특수 언어입니다. 아무도 BLR로 직접 작성하지 않습니다. 최고의 실행 속도를 위해 이 언어에서는 소위 역폴란드 표기법이 사용되기 때문에 직접 작성하는 것은 상당히 어려울 것입니다. 다음은 간단한 예입니다:

blr_begin,

     blr\_assignment,

        blr\_field, 0, 7, 'D','A','T','E','I','Z','M',

        blr\_variable, 1,0,

     blr\_assignment,

        blr\_field, 0, 4, 'R','A','T','E',

        blr\_variable, 0,0,

     blr\_block,

쿼리, 프로시저, 트리거 및 기타 트리거에 대한 BLR은 서버 커널의 일부인 특수 전처리기에 의해 생성됩니다. 표 7에 표시된 것처럼 뷰의 경우 텍스트(초기) 뷰와 컴파일된 뷰, 즉 BLR이 저장됩니다. BLR을 가진 객체를 참조할 때 서버는 객체의 이진 코드를 실행하며 매번 이러한 객체의 초기 텍스트를 해석하지 않으므로 복잡한 쿼리의 실행 속도를 높일 수 있습니다.

InterBase의 객체 계층 구조

데이터베이스 객체가 무엇을 나타내는지 명확히 이해하기 위해 «무엇이 무엇을 포함하는가»라는 원칙에 따라 데이터베이스 객체의 계층 구조를 만들어 보겠습니다. 데이터베이스 파일의 물리적 페이지는 데이터 구성의 최하위 수준으로서 계층 구조에 가장 먼저 포함되어야 합니다. 그 다음으로 테이블은 다른 모든 유형의 객체를 설명하는 기본 객체로 사용됩니다. 테이블은 저장 프로시저, 트리거, 계산 필드, 유효성 검사, 표현식 인덱스, 예외 등을 설명합니다. 주의하세요 - 설명만 할 뿐입니다! 테이블에는 이러한 객체의 선언과 정의만 포함되며, 객체는 BLR을 통해 구현됩니다. 따라서 테이블을 다른 모든 데이터베이스 객체를 지지하는 프레임 형태로 표현할 수 있습니다. BLR은 구현 계층으로서 프레임의 맨 아래에 위치하며, 그 위에 트리거, 저장 프로시저, 표현식 인덱스, 뷰가 있습니다.

(뷰와 같은) 많은 객체의 BLR이 시스템 테이블에 저장된다고 이의를 제기할 수 있는 InterBase 내부 구조 전문가들을 안심시키기 위해, 이러한 태도는 그림으로 표현하기 상당히 어렵고 단순화를 위해 생략한다는 점을 언급하겠습니다. 이 도식은 데이터베이스 객체 간의 상호 의존성을 절대적으로 정확하게 재현하는 것을 목표로 하지 않으며, 단지 그들의 긴밀한 상호 연결을 설명할 뿐입니다.

이러한 객체 유형들이 중간 논리 없이 이를 구현하는 BLR과 직접 연결되어 있다는 사실이 이들을 하나로 묶습니다. 예외는 별도로 분류되어야 합니다 - 예외는 사용자가 정의한 특수한 유형의 오류를 나타냅니다. 예외는 InterBase 커널 수준에서 처리되므로 BLR을 가지지 않습니다. 제약 조건과 같은 유형의 제약은 트리거 위에 배치됩니다. 실제로 트리거가 제약 조건과 검사의 논리를 구현하기 때문입니다.

데이터베이스 논리적 및 물리적 구조의 객체 계층 구조는 그림 2에 그려져 있습니다.

그림 10. InterBase 데이터베이스 논리 구조의 객체

물론 이 도식은 데이터베이스의 논리적 구조와 객체 간의 상호 연결을 대략적으로만 설명하며 일반적인 개념을 제공합니다. InterBase 데이터베이스 메타데이터의 구조를 연구하려는 사람은 누구나 데이터베이스 시스템 테이블의 리엔지니어링을 실행하고 객체 간의 모든 상호 연결을 고려할 수 있으며, 문서와 InterBase 기본 코드를 참조할 수도 있습니다. 이 표는 주요 데이터베이스 객체만 보여줍니다. 이러한 객체가 데이터베이스에서 수행하는 주요 기능을 간략히 설명하겠습니다.

테이블 - 사용자 및 시스템 데이터를 포함하는 주요 객체입니다. 테이블은 고유한 이름을 가지며 명명된 필드 집합을 포함합니다. 사용자는 테이블에 데이터를 배치하고, 추출하고, 수정할 수 있습니다. 테이블은 손으로 그린 일반 종이 테이블과 유사하다고 말할 수 있습니다.

트리거 - 데이터 작업 시 추가 작업을 구현하는 데 사용되는 실행 가능한 코드 부분입니다. 트리거는 삽입, 수정 또는 삭제 작업 전후에 실행되며, 새로 생성된 레코드에 값 대체 및 기타 여러 기능을 실현할 수 있습니다.

저장 프로시저는 데이터베이스 수준에서 비즈니스 로직을 구현하는 강력한 도구입니다. 서버 수준에서 실행되므로 매우 빠르게 작동하며 데이터 집합에 대한 일련의 작업을 실행할 수 있습니다. InterBase 저장 프로시저는 다른 테이블과의 통합을 포함한 모든 SQL 작업이 가능한 표준 SQL 데이터 집합을 반환합니다.

뷰는 서버에서 실행되는 컴파일된 SQL 쿼리입니다. 뷰는 데이터 집합을 구성하고 비즈니스 로직의 일부를 서버로 전송할 수 있게 합니다.

유효성 검사는 테이블의 필드 값에 설정된 제약 조건입니다. 예를 들어 특정 필드가 양수 값만 허용하도록 지정할 수 있습니다. 필드 값에 대한 제약 조건은 트리거에 의해 구현되며 데이터베이스 수준에서 참조 무결성을 효과적으로 제어할 수 있습니다. 일반적으로 제약 조건은 테이블에 잘못된 값이 배치되는 것을 방지하는 데 사용됩니다.

사용자 - InterBase를 사용하면 데이터베이스 작업을 위한 여러 사용자를 가질 수 있으며 다양한 데이터베이스 객체에 대한 액세스 권한을 사용자 간에 분배할 수 있습니다. 따라서 특정 데이터베이스 작업에 대한 권한을 제어할 수 있습니다.

사용자 정의 함수(UDF) - 사용자가 정의한 함수입니다. 이는 표준 SQL 인터페이스를 자체 함수로 확장할 수 있게 해주는 InterBase의 가장 강력한 기능 중 하나입니다. 예를 들어 UPPER(모든 기호를 대문자로 설정)와 같은 문자열 작업 함수는 InterBase 세트에 포함된 표준 UDF 라이브러리에 구현되어 있습니다. 자체 UDF를 생성할 수 있는 가능성 덕분에 개발자는 거의 모든 함수로 InterBase 기능을 확장할 수 있습니다. UDF 생성을 위해 동적 라이브러리를 생성할 수 있는 모든 프로그래밍 환경(Visual C++, C++ Builder, Delphi 등)을 사용할 수 있습니다.

결론

이 장에서는 InterBase 데이터베이스 내에서 데이터 저장 및 처리 구현에 관한 질문을 처음으로 고려했습니다. 불행히도 많은 용어와 부정확한 비유를 적용하지 않고 이 주제를 간략히 검토할 수는 없습니다. 데이터베이스 물리적 및 논리적 구조를 더 자세히 설명하려면 어쨌든 InterBase 기본 코드를 참조해야 하지만, 그것은 또 다른 책이 될 것입니다.

그럼에도 불구하고 모든 프로그래머가 매일 사용하는 제품의 내용을 숙지하는 것이 유용할 것이라고 생각합니다.

참고 문헌

  1. «The On-Disk Structure of InterBase» by Ann.W.Harrison

  2. «Space Management in InterBase» by Ann W.Harrison

  3. «Structure of a Data Page» by Paul Beach (With thanks to Dave Schnepper and Deej Bredenberg)