Column: 스키마가 정의하는 속성 단위와 컬럼 지향 저장 방식의 차이
컬럼(Column)은 관계형 테이블에서 같은 타입과 제약을 가진 속성의 집합이다. 논리적으로는 스키마의 한 축이지만, 물리적으로는 행 지향(row-oriented)과 컬럼 지향(column-oriented) 저장 방식으로 나뉘어 성능 특성이 크게 달라진다. 이 글에서는 컬럼의 타입, 제약, 저장 방식, 인덱스와의 관계를 PostgreSQL 기준으로 정리한다
By 송민성
1. 개념
컬럼(Column)은 관계형 테이블에서 이름, 데이터 타입(data type), 제약 조건(constraint)이 정해진 속성 하나를 가리킨다. 테이블은 컬럼의 집합으로 구조(스키마, schema)가 정의되고, 각 행(row)은 컬럼마다 값을 하나씩 가진다.
컬럼이 정의하는 것은 다음과 같다.
- 이름: 테이블 안에서 유일한 식별자
- 타입:
integer,text,timestamptz,jsonb등 저장 가능한 값의 종류 - 제약 조건:
NOT NULL,UNIQUE,CHECK,PRIMARY KEY,FOREIGN KEY,DEFAULT - 순서: 테이블 생성 시점의 위치(
attnum). 쿼리 의미에는 영향이 없다.
2. 왜 사용하는가
- 무결성(integrity) 보장: 타입과 제약을 DB가 강제하므로 애플리케이션 버그가 있어도 잘못된 데이터가 저장되기 어렵다.
- 최적화 정보 제공: 타입, 널 허용 여부, 통계(statistics)를 바탕으로 쿼리 옵티마이저(optimizer)가 실행 계획을 세운다.
- 저장 효율: 고정 길이 타입은 크기가 일정해 저장과 접근이 단순하다.
- 인덱스 단위: 인덱스는 하나 이상의 컬럼(또는 표현식)을 기준으로 만든다.
3. 동작 원리
3.1 논리적 구조와 물리적 저장
PostgreSQL은 기본적으로 행 지향(row-oriented) 저장 방식이다. 한 행의 모든 컬럼 값이 힙(heap) 페이지 안에 이어서 저장된다. 기본 페이지 크기는 8KB다.
| 구분 | 행 지향 | 컬럼 지향 |
|---|---|---|
| 저장 단위 | 행 전체를 연속 저장 | 컬럼별로 값을 연속 저장 |
| 유리한 작업 | 한 행 전체를 읽고 쓰는 OLTP | 일부 컬럼을 대량 집계하는 OLAP |
| 압축 | 상대적으로 제한적 | 같은 타입 값이 모여 있어 압축 효율이 높음 |
| 대표 시스템 | PostgreSQL, MySQL(InnoDB) | ClickHouse, Parquet 포맷, Amazon Redshift |
예를 들어 SELECT avg(price) FROM orders는 price 컬럼만 필요하다. 행 지향에서는 페이지 전체를 읽어야 하지만, 컬럼 지향에서는 price 데이터만 읽으면 된다.
3.2 컬럼 타입과 행 레이아웃
PostgreSQL은 고정 길이 타입(integer는 4바이트, bigint는 8바이트)과 가변 길이 타입(text, varchar)을 구분한다. 컬럼 순서에 따라 정렬 패딩(alignment padding)이 생겨 행 크기가 달라질 수 있다. 큰 가변 길이 값은 TOAST라는 별도 저장소로 분리되기도 한다.
3.3 NULL 처리
PostgreSQL은 행 헤더에 널 비트맵(null bitmap)을 두어 NULL인 컬럼을 표시하고, NULL 값 자체에는 데이터 공간을 쓰지 않는다. NULL은 "값 없음/알 수 없음"을 뜻하며 = NULL이 아니라 IS NULL로 비교해야 한다.
4. 코드 예제
-- 컬럼 정의: 타입, 제약, 기본값
CREATE TABLE orders (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
user_id bigint NOT NULL,
status text NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'paid', 'cancelled')),
price numeric(12,2) NOT NULL CHECK (price >= 0),
memo text,
created_at timestamptz NOT NULL DEFAULT now()
);
-- 컬럼 추가 (PostgreSQL 11+에서 상수 DEFAULT를 가진 NOT NULL 컬럼 추가는 테이블 전체 재작성이 필요 없다)
ALTER TABLE orders ADD COLUMN currency char(3) NOT NULL DEFAULT 'KRW';
-- 컬럼 단위 인덱스와 복합 인덱스
CREATE INDEX idx_orders_user_created ON orders (user_id, created_at DESC);
-- 컬럼 메타데이터 조회
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = 'orders'
ORDER BY ordinal_position;
-- 컬럼 타입 변경 (경우에 따라 테이블 재작성 발생)
ALTER TABLE orders ALTER COLUMN memo TYPE varchar(500);
-- NULL 비교는 IS NULL 사용
SELECT id FROM orders WHERE memo IS NULL;5. 시간 복잡도 또는 성능 특성
컬럼 자체의 연산 복잡도는 하나로 정해지지 않는다. 다음과 같은 특성을 이해하면 된다.
- SELECT 컬럼 수: 행 지향 저장에서는 일부 컬럼만 조회해도 페이지 단위로 읽기 때문에 디스크 I/O는 크게 줄지 않는다. 다만 네트워크 전송량과 메모리 사용은 줄어든다. 인덱스만으로 답할 수 있는 인덱스 전용 스캔(index-only scan)은 힙 접근을 피할 수 있다.
- B-tree 인덱스 조회: 일반적으로 O(log N)이다.
- 컬럼 추가: PostgreSQL 11 이후 상수 기본값을 가진 컬럼 추가는 메타데이터 변경 위주라 빠르다.
volatile기본값 함수(예:random())는 테이블 재작성이 필요할 수 있다. - 타입 변경: 바이너리 호환되지 않는 변환은 테이블 재작성과 강한 락(
ACCESS EXCLUSIVE)이 필요하다. - 컬럼 지향 저장: 같은 타입 값이 연속 배치되어 압축과 벡터화 처리(vectorized execution)에 유리하며, 대량 집계에서 행 지향보다 I/O를 크게 줄일 수 있다. 반면 단건 INSERT/UPDATE는 여러 컬럼 영역을 건드려야 해 불리하다.
6. 실무 사용 사례
- OLTP 서비스 DB: 주문, 회원 등은 행 지향 저장의 PostgreSQL이나 MySQL을 쓰고, 컬럼 제약으로 무결성을 확보한다.
- 분석 파이프라인: 로그나 이벤트 데이터를 Parquet로 저장하거나 ClickHouse, Redshift에 적재해 필요한 컬럼만 읽는다.
- 복합 인덱스 설계: 조회 패턴에 맞춰 컬럼 순서를 정한다. 위 예제의
(user_id, created_at DESC)는user_id조건 후 최신순 조회에 맞는다. - 무중단 스키마 변경: 컬럼 추가는 nullable로 먼저 만들고, 배치로 값을 채운 뒤 제약을 거는 단계적 방식을 쓴다.
7. 주의할 점
- **
SELECT *지양**: 불필요한 컬럼 전송과 인덱스 전용 스캔 무효화를 부른다. 스키마 변경 시 결과 형태도 바뀐다. - NULL 의미 혼동: 빈 문자열과 NULL은 다르다.
NULL = NULL은 참이 아니라 NULL(unknown)이다.NOT IN서브쿼리에 NULL이 섞이면 결과가 비는 문제가 생긴다. - 타입 변경 락: 대형 테이블의
ALTER COLUMN ... TYPE은 장시간 락을 잡을 수 있으므로 운영 환경에서는 사전 검증이 필요하다. - 컬럼 순서와 패딩: 고정 길이 큰 타입을 앞쪽에 배치하면 패딩을 줄여 저장 공간을 아낄 수 있다. 다만 이미 운영 중인 테이블에서는 변경 비용이 크다.
- JSONB 남용: 자주 조회하고 조건으로 쓰는 값까지
jsonb에 넣으면 타입·제약·통계의 이점을 잃는다. 핵심 속성은 일반 컬럼으로 분리한다. - 정밀도: 금액에
float/double precision을 쓰면 오차가 생긴다.numeric을 사용한다.
8. 핵심 정리
- 컬럼은 이름, 타입, 제약으로 정의되는 테이블의 속성 단위이며 무결성과 최적화의 기반이다.
- PostgreSQL은 행 지향 저장이고, 분석용 시스템은 컬럼 지향 저장으로 대량 집계 I/O와 압축 효율을 높인다.
- NULL은 널 비트맵으로 표시되며 비교는
IS NULL로 한다. - 컬럼 추가와 타입 변경은 버전과 조건에 따라 비용이 크게 다르므로 운영 환경에서는 락과 재작성 여부를 확인한다.
- 조회 패턴에 맞는 복합 인덱스 컬럼 순서와 정확한 타입 선택이 성능과 정합성을 좌우한다.