[Database] 내부 저장 구조
관계형 데이터베이스에서 테이블을 정의하고 데이터를 저장할 때, 일부 데이터베이스에서는 테이블의 PK와는 별개로 내부적으로 rowid라는 개념을 사용해서 데이터를 저장한다.
사용자가 테이블을 정의할 때 row_id 라는 컬럼을 지정하지 않았지만 데이터베이스가 내부적으로 row_id를 컬럼을 만들고 각 row을 식별할 때 사용한다.
이 rowid는 row의 물리적인 위치를 가리키고, 위치 기반 접근으로 디스크에서 데이터를 더 빠르게 찾고 조작할 수 있다.
rowid 개념을 사용하는 데이터베이스는 아래와 같다.
SQLite
모든 테이블은 기본적으로 rowid라는 숨겨진 컬럼을 가지고, 이 rowid로 각 행을 식별한다.
rowid 기반 쿼리는 매우 빠르게 동작해 대규모 데이터셋에서 유리하다.
PostgreSQL
rowid와 유사한 개념으로 ctid를 사용한다.
ctid는 각 row의 물리적 위치를 나타내는 필드로, 해당 row가 저장된 테이블의 블록 번호와 튜플 인덱스로 구성된다.
블록 번호는 디스크상의 테이블 블록을 가리키고 튜플 인덱스는 블록 내에서의 row의 위치를 나타낸다.
ctid를 사용해 특정 row를 빠르게 찾거나 중복된 row을 찾을 수 있다.
Oracle
ROWID라는 고유한 식별자를 사용한다.
ROWID는 데이터의 물리적인 위치를 나타내는 18자리 문자열으로, 역시 데이터를 빠르게 찾거나 중복 제거 등 시스템 수준의 작업을 수행할 때 사용된다.
이제 데이터베이스의 디스크에서 데이터를 어떻게 저장하는지 살펴보자.
데이터베이스에서 row는 페이지 라고 불리는 공간에 저장된다.
페이지는 디스크에서 데이터를 효율적으로 관리하기 위한 기본 단위로, 데이터를 저장하는 기본 물리적 단위이다.
PostgreSQL, MySQL, Oracle 등 대부분의 RDB가 페이지 기반 저장 방식을 사용하고 데이터베이스마다 페이지가 차지하는 크기가 다르다. (PostgreSQL은 8KB를 가진다)
하나의 페이지에 여러 row가 포함될 수 있고, 데이터베이스는 페이지 단위로 입출력을 다뤄 한 번의 디스크 I/O로 여러 데이터를 다룰 수 있어 효율적이다.
디스크에서 데이터를 읽는 작업을 I/O라고 표현하는데, 디스크에 직접 접근하는 작업은 CPU나 메모리 작업에 비해 비용이 높아 데이터베이스의 성능에 크게 영향을 준다.
이 페이지들은 heap 이라고 불리는 자료구조에 저장된다.
heap은 특정한 구조를 가지지 않는 데이터의 모음으로 테이블의 데이터가 순서 없이 저장된다.
페이지들의 집합을 heap으로 생각하면 된다.
순서 없이 저장되기에 데이터를 삽입할 때는 테이블의 끝에 추가하거나 삭제된 데이터가 차지하던 공간을 재사용한다.
heap에서 원하는 데이터를 찾아올 때는 문제가 된다.
순서가 없으니 heap에 포함되는 페이지를 full scan 하며 조건에 부합하는 데이터를 찾아야 하는데, 이 부분에서 성능을 끌어올리기 위해 인덱스가 도입됐다.
인덱스는 heap의 특정 부분을 가리키는 포인터라고 생각하면 된다.
마찬가지로 페이지로 저장되지만 row들이 실제로 저장되는 heap 과는 별개로 B-Tree나 HashTable 등의 자료구조로 저장되고, 이 구조로 검색 효율을 최적화한다. (디스크 상에 페이지로 저장하고, B-Tree 같은 자료구조를 사용한다)
테이블의 데이터 row를 빠르게 찾기 위한 참조 정보를 가지며 데이터베이스 시스템은 테이블 데이터와 인덱스를 독립적으로 관리해 데이터를 조작함에 따라서 인덱스도 함께 업데이트 해 준다.
인덱스를 조회할 때도 디스크 I/O가 발생하지만, 전체 데이터 검색에 필요한 I/O 보다는 훨씬 적은 I/O로 필요한 데이터를 찾을 수 있어 효율적이다.
EMP_ID에 인덱스를 만들어 EMP_ID 값이 10에 해당하는 데이터는 rowid가 1이고 0번 페이지에 있음을 가리킨다.
WHERE EMP_ID = 10 조건을 추가해서 쿼리를 실행하면 0번 페이지로 가서 데이터를 가져오는데, 이 때 페이지에서 불필요한 row들은 메모리에서 자동으로 폐기되거나 버퍼 풀에 유지된다. (캐싱)
SELECT EMP_ID FROM TABLE WHERE EMP_ID = 10; 해당 쿼리를 실행하는 경우 EMP_ID 컬럼에는 이미 인덱스가 존재하니 heap에 접근할 필요 없이 인덱스에서 모든 필요한 데이터를 가져올 수 있어 디스크 I/O를 줄여 성능을 크게 향상시킬 수 있다.
해당 쿼리가 어떤 인덱스를 사용하고 어떤 전략을 사용하는지 확인하려면 EXPLAIN 과 ANALYZE 를 사용하면 된다.
디스크에서 heap 공간은 정렬되지 않은 상태지만, 데이터베이스에 따라서 heap 대신 다른 정렬된 공간으로 사용하는 경우도 있다.
Index Organized Table
데이터가 인덱스와 같은 순서로 저장되는 테이블으로, 테이블 자체가 인덱스 구조로 구성된다.
Clustered Index와는 다르게 데이터와 인덱스가 함께 저장된다.
Oracle에서 사용하는 방식이다. 테이블의 데이터를 인덱스 키 순서대로 저장하고 접근할 수 있다.
보조 인덱스를 생성해서 사용할 수 있다.
Clustered Index
테이블의 데이터가 인덱스의 키 값에 따라 정렬된 후 저장된다.
heap 처럼 데이터와 인덱스를 물리적으로 분리해서 저장한다.
SQL Server와 MySQL의 InnoDB 엔진이 사용하는 방식이다. 테이블 당 하나의 Clustered Index만 존재할 수 있다.
역시 보조 인덱스를 생성해서 사용할 수 있다.
Heap
언급한대로 데이터를 순서 없이 저장하는 구조로 별도의 인덱스가 존재할 수 있다.
PostgreSQL과 MySQL의 MyISAM 엔진이 사용하는 방식이다.
IoT / Clustered Index / Heap 모두 페이지 단위로 row를 관리하는건 동일하다.
데이터베이스의 설계 철학과 성능 최적화 전략에 따라 데이터를 저장하는 방식이 다르니 데이터베이스를 선택할 때 시스템이 제공하는 저장 방식의 특성을 제대로 이해한 후 상황에 적합한 데이터베이스를 사용하자.
데이터베이스 시스템은 행 기반 데이터베이스와 열 기반 데이터베이스로 나뉜다.
테이블에는 컬럼과 여러 row들로 구성된다. 여기서의 row를 레코드라고 하자.
행 기반 데이터베이스에서는 레코드를 모든 컬럼의 데이터를 포함해서 저장한다.
하나의 레코드에는 해당 레코드의 모든 컬럼 값이 함께 저장되고, 이름 / 나이 / 주소 라는 컬럼이 있고 각각 a / b / c 데이터를 가진다면 디스크에 데이터가 저장될 때는 a / b / c 레코드가 하나의 단위로 저장된다.
즉, 모든 컬럼 값이 하나의 페이지에 함께 저장된다. (여러 레코드가 저장될 수 있다)
행 단위 데이터 작업에 최적화돼 트랜잭션을 빠르게 처리할 수 있고, 복잡한 쿼리와 조인 연산에 강하다.
SELECT * FROM TABLE; 과 SELECT 이름 FROM TABLE; 을 비교했을 때 행 기반 데이터베이스에서는 해당 행의 전체 데이터가 읽히니 디스크 I/O 에서는 큰 차이가 없지만, SELECT * FROM TABLE; 로 전체 데이터를 가져오는 경우 메모리 사용량과 네트워크 트래픽 측면에서 비효율적이다.
Oracle, MySQL, PostgreSQL 등이 행 기반 데이터베이스이다.
Online transaction Processing에 유리하다.
열 기반 데이터베이스에서는 각 컬럼의 데이터가 별도로 저장된다.
따라서 같은 컬럼에 속한 데이터가 서로 인접하게 저장될 수 있다.
이름 / 나이 / 주소 컬럼이 있다면 각 컬럼은 독립적인 저장 공간을 가지게 돼 이름 데이터를 한 덩어리로 저장하고, 나이 데이터를 한 덩어리로 저장한다.
즉, 컬럼의 데이터를 별도의 페이지에 저장한다.
데이터를 압축해서 보관할 수 있고, 대규모 스캔 작업에 최적화돼 집계 연산에 강하다.
실시간 분석, 빅데이터 처리에 자주 사용된다.
Apache Cassandra, Apache HBase 등이 열 기반 데이터베이스이다.
Online Analytical Processing에 유리하다.
'Database > Database' 카테고리의 다른 글
[Database] 동시성 처리 (0) | 2024.04.13 |
---|---|
[Database] Sharding (0) | 2024.04.08 |
[Database] Partitioning (0) | 2024.04.04 |
[Database] B+Tree 자료구조 (0) | 2024.03.30 |
[Database] ACID (1) | 2024.01.21 |
댓글
이 글 공유하기
다른 글
-
[Database] Sharding
[Database] Sharding
2024.04.08 -
[Database] Partitioning
[Database] Partitioning
2024.04.04 -
[Database] B+Tree 자료구조
[Database] B+Tree 자료구조
2024.03.30 -
[Database] ACID
[Database] ACID
2024.01.21