PostgreSQL 2200H 오류 원인과 해결 방법 완벽 가이드

2200H
2026년 06월 12일 | DBMS Error 가이드

이 글에서 다루는 내용

2200H 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

2200H sequence generator limit exceeded 는?

PostgreSQL 에러 코드 2200H시퀀스 생성기(Sequence Generator)의 한계값을 초과했을 때 발생하는 에러입니다. 시퀀스는 자동으로 증가하는 고유 번호를 생성하는 데이터베이스 객체인데, 설정된 최댓값(MAXVALUE) 또는 최솟값(MINVALUE)에 도달한 이후에도 계속 값을 요청하면 이 에러가 트리거됩니다. 특히 SERIAL, BIGSERIAL, 또는 명시적으로 생성한 SEQUENCE를 사용하는 테이블에서 대량의 INSERT 작업이 반복될 때 실무 환경에서 빈번히 마주치는 문제입니다.


주요 발생 원인

1. SERIAL (INT4) 타입의 최댓값 도달

SERIAL 타입은 내부적으로 INTEGER를 사용하며, 최댓값은 약 21억(2,147,483,647) 입니다. 대용량 트랜잭션 시스템, 로그 테이블, 이벤트 트래킹 테이블처럼 하루에도 수백만 건의 레코드가 쌓이는 환경에서는 생각보다 빠르게 이 한계에 도달합니다. 특히 테스트 환경에서 대량 데이터를 반복 삽입하고 삭제하더라도 시퀀스 값은 롤백되지 않기 때문에, 개발 중에 값을 빠르게 소진하는 경우도 많습니다.

2. 시퀀스의 CYCLE 옵션 미설정 또는 잘못된 MAXVALUE 설정

시퀀스를 생성할 때 NO CYCLE 옵션(기본값)을 사용하면, 최댓값에 도달한 순간 더 이상 새로운 값을 발급하지 않고 에러를 반환합니다. 또한 업무 요건에 맞지 않게 너무 낮은 MAXVALUE를 수동으로 설정한 경우, 예상보다 훨씬 빨리 한계에 도달하여 서비스 장애로 이어질 수 있습니다. 이 경우 시퀀스의 현재 값과 최댓값의 차이를 주기적으로 모니터링하지 않으면 장애 발생 전까지 인지하기 어렵습니다.

3. 애플리케이션의 잦은 시퀀스 nextval() 호출과 롤백

트랜잭션이 실패하거나 롤백되어도, 이미 발급된 시퀀스 값은 회수되지 않고 영구적으로 소비됩니다. 이는 PostgreSQL의 시퀀스가 트랜잭션과 독립적으로 동작하도록 설계된 특성 때문입니다. 배치 작업이나 재시도 로직이 잦은 애플리케이션에서는 실제 테이블에 저장된 레코드 수보다 훨씬 빠르게 시퀀스 값이 소진될 수 있으며, 이로 인해 한계 도달 시점이 크게 앞당겨집니다.


해결 방법

해결책 1: SERIALBIGSERIAL로 컬럼 타입 변경

BIGSERIALBIGINT를 기반으로 하며 최댓값이 약 922경(9,223,372,036,854,775,807) 으로, 사실상 고갈될 가능성이 거의 없습니다.

-- 현재 시퀀스 현황 확인
SELECT
    sequencename,
    last_value,
    max_value,
    (max_value - last_value) AS remaining
FROM pg_sequences
WHERE schemaname = 'public';

-- 기존 SERIAL 컬럼을 BIGINT로 변경 (다운타임 필요)
ALTER TABLE orders ALTER COLUMN order_id TYPE BIGINT;

-- 기존 시퀀스의 최댓값을 BIGINT 범위로 확장
ALTER SEQUENCE orders_order_id_seq MAXVALUE 9223372036854775807;

-- 변경 후 확인
SELECT * FROM pg_sequences WHERE sequencename = 'orders_order_id_seq';

해결책 2: 시퀀스 최댓값(MAXVALUE) 직접 수정

기존 시퀀스를 삭제하지 않고 최댓값만 늘릴 수 있습니다. 서비스 중단 없이 즉시 적용 가능합니다.

-- 현재 시퀀스 상태 점검
SELECT
    s.sequencename,
    s.last_value,
    s.max_value,
    ROUND((s.last_value::NUMERIC / s.max_value) * 100, 2) AS used_pct
FROM pg_sequences s
WHERE s.schemaname = 'public'
ORDER BY used_pct DESC;

-- 시퀀스 최댓값 변경 (서비스 중단 없이 적용 가능)
ALTER SEQUENCE orders_order_id_seq MAXVALUE 9223372036854775807;

-- CYCLE 옵션 추가 (최댓값 도달 후 1부터 재시작, 주의해서 사용)
-- 중복 키 문제가 발생할 수 있으므로 신중히 적용
ALTER SEQUENCE orders_order_id_seq CYCLE;

해결책 3: PostgreSQL 10+ IDENTITY 컬럼으로 전환

IDENTITY 컬럼은 SQL 표준을 따르며 더 안전하고 명확한 시퀀스 관리를 제공합니다.

-- 새 테이블 생성 시 IDENTITY 사용 (권장)
CREATE TABLE new_orders (
    order_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    customer_id INT NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);

-- 기존 테이블을 IDENTITY 컬럼으로 마이그레이션
-- Step 1: 기존 DEFAULT 제거
ALTER TABLE orders ALTER COLUMN order_id DROP DEFAULT;

-- Step 2: 기존 시퀀스 삭제
DROP SEQUENCE IF EXISTS orders_order_id_seq;

-- Step 3: IDENTITY 컬럼으로 변환
ALTER TABLE orders
    ALTER COLUMN order_id ADD GENERATED ALWAYS AS IDENTITY;

-- Step 4: 현재 최댓값으로 시퀀스 재시작
SELECT SETVAL(
    pg_get_serial_sequence('orders', 'order_id'),
    (SELECT MAX(order_id) FROM orders)
);

해결책 4: 긴급 복구 – 시퀀스 값 즉시 재설정

이미 에러가 발생한 상황에서 서비스를 빠르게 복구해야 할 때 사용합니다.

-- 현재 테이블의 최대 ID 값 확인
SELECT MAX(order_id) FROM orders;

-- 시퀀스를 현재 최댓값 이후로 재설정
SELECT SETVAL('orders_order_id_seq', (SELECT MAX(order_id) FROM orders) + 1, false);

-- 또는 특정 값으로 직접 설정
SELECT SETVAL('orders_order_id_seq', 1000000000);

예방 방법

1. 시퀀스 사용량 모니터링 자동화

시퀀스 고갈은 사전에 충분히 감지할 수 있습니다. 아래 쿼리를 Prometheus, Zabbix, 또는 사내 모니터링 도구에 등록하여 사용률이 80% 이상이 되면 알림을 받도록 설정하세요.

-- 시퀀스 사용률 80% 이상인 항목 감지 (모니터링 쿼리)
SELECT
    schemaname,
    sequencename,
    last_value,
    max_value,
    ROUND((last_value::NUMERIC / NULLIF(max_value, 0)) * 100, 2) AS used_pct,
    (max_value - last_value) AS remaining_values
FROM pg_sequences
WHERE
    last_value IS NOT NULL
    AND (last_value::NUMERIC / NULLIF(max_value, 0)) >= 0.8
ORDER BY used_pct DESC;

2. 신규 테이블 설계 시 BIGINT 또는 UUID 기본 채택

테이블 설계 단계에서부터 PK 타입을 BIGINT GENERATED ALWAYS AS IDENTITY 또는 UUID로 표준화하는 정책을 수립하세요. SERIAL(INT4)은 레거시 타입으로 간주하고 신규 개발에서는 사용을 지양합니다. UUID를 사용하면 시퀀스 고갈 문제 자체가 원천 차단됩니다.

-- UUID 기반 PK 사용 예시 (pgcrypto 또는 gen_random_uuid 활용)
CREATE TABLE events (
    event_id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    event_type VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);

관련 에러

  • 23505 (unique_violation): 시퀀스 값이 고갈된 후 CYCLE 옵션으로 재시작되면, 이미 존재하는 PK 값과 충돌하여 고유 키 위반 에러가 발생할 수 있습니다.
  • 42P07 (duplicate_table): 시퀀스 마이그레이션 과정에서 동일한 이름의 시퀀스를 재생성하려 할 때 발생합니다.
  • 55000 (object_not_in_prerequisite_state): 이미 종료된 시퀀스에 nextval()을 호출할 때 발생하는 상태 에러로, 2200H와 혼동되는 경우가 있습니다.

DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기