본문 바로가기
카테고리 없음

PostgreSQL JSONB

by DoRightting 2024. 11. 9.

1. JSONB 기본 개념

JSONB는 PostgreSQL에서 제공하는 이진 형식의 JSON 데이터 타입입니다. 일반 JSON과 달리 파싱이 완료된 이진 형태로 저장되어 다음과 같은 장점이 있음

-- JSONB 컬럼 생성 예시
CREATE TABLE prompt_configs (
    id SERIAL PRIMARY KEY,
    config JSONB,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

주요 특징

  1. 빠른 처리 속도
    • 이진 형식 저장으로 재파싱 불필요
    • 인덱싱 지원으로 빠른 검색
  2. 공간 효율성
    • 중복 키 제거
    • 불필요한 공백 제거
  3. 강력한 쿼리 기능
  4. -- 특정 키 검색 SELECT * FROM prompt_configs WHERE config ? 'temperature'; -- 경로 기반 검색 SELECT config->'model'->'parameters'->>'temperature' FROM prompt_configs;

2. 실무 적용 예시

2.1 프롬프트 설정 저장

@Entity
@Table(name = "prompts")
public class Prompt {
    @Type(JsonBType.class)
    @Column(columnDefinition = "jsonb")
    private Map<String, Object> configuration;

    // 설정 예시
    {
        "model": {
            "name": "gpt-3.5-turbo",
            "parameters": {
                "temperature": 0.7,
                "maxTokens": 2000,
                "topP": 1
            }
        },
        "filters": ["profanity", "sensitive-content"],
        "customInstructions": ["Be concise", "Use simple language"]
    }
}

2.2 효율적인 쿼리 작성

-- 인덱스 생성
CREATE INDEX idx_config ON prompt_configs USING GIN (config);

-- 복잡한 조건 검색
SELECT * FROM prompt_configs
WHERE config @> '{"model": {"name": "gpt-3.5-turbo"}}'
  AND (config#>>'{parameters,temperature}')::float > 0.5;

2.3 JPA와의 통합

@Converter(autoApply = true)
public class JsonBConverter implements AttributeConverter<Map<String, Object>, String> {
    private static final ObjectMapper mapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(Map<String, Object> attribute) {
        try {
            return mapper.writeValueAsString(attribute);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("JSON 변환 실패", e);
        }
    }

    @Override
    public Map<String, Object> convertToEntityAttribute(String dbData) {
        try {
            return mapper.readValue(dbData, new TypeReference<Map<String, Object>>() {});
        } catch (JsonProcessingException e) {
            throw new RuntimeException("JSON 파싱 실패", e);
        }
    }
}

3. 성능 최적화 전략

3.1 인덱싱 전략

-- 전체 문서 인덱싱
CREATE INDEX idx_config_gin ON prompt_configs USING GIN (config);

-- 특정 키에 대한 인덱싱
CREATE INDEX idx_config_model ON prompt_configs USING GIN ((config->'model'));

3.2 쿼리 최적화

-- 효율적인 쿼리
SELECT * FROM prompt_configs
WHERE config @> '{"filters": ["profanity"]}';

-- 비효율적인 쿼리
SELECT * FROM prompt_configs
WHERE config->>'filters' LIKE '%profanity%';  -- 인덱스 사용 불가

JSONB와 일반 JSON 타입의 차이점

  1. 저장 방식
    • JSON: 입력된 텍스트를 그대로 저장
    • JSONB: 파싱된 이진 형식으로 저장
  2. 성능 특성
    • JSON: 저장은 빠르지만 매 쿼리마다 재파싱 필요
    • JSONB: 초기 저장은 느리지만 쿼리 처리가 빠름
  3. 실무적 장점
    • 인덱싱을 통한 빠른 검색
    • 공간 효율적인 저장
    • 중복 키 자동 제거
  4. -- JSONB는 인덱싱 지원 CREATE INDEX idx_config ON configs USING GIN (data);

JSONB 인덱싱 전략

  1. GIN 인덱스 활용**
  2. -- 전체 문서 검색이 필요한 경우 CREATE INDEX idx_full ON configs USING GIN (data); -- 특정 키에 대한 검색이 빈번한 경우 CREATE INDEX idx_specific ON configs USING GIN ((data->'specific_key'));
  3. 부분 인덱스 활용
  4. -- 특정 조건의 데이터만 인덱싱 CREATE INDEX idx_partial ON configs USING GIN (data) WHERE (data->>'status') = 'active';
  5. 성능 모니터링
    • 실행 계획 분석
    • 인덱스 사용률 추적
    • 주기적인 VACUUM ANALYZE 수행

JSONB를 사용할 때의 주의점

  1. 스키마 관리
    • 완전한 스키마리스가 아닌 하이브리드 접근
    • 주요 필드는 일반 컬럼으로 관리
    • 변동성 높은 데이터만 JSONB로 저장
  2. 성능 고려사항
    • 깊은 중첩 구조 지양
    • 자주 검색되는 필드는 일반 컬럼으로 분리
    • 적절한 인덱싱 전략 수립
  3. @Query("SELECT p FROM Prompt p WHERE p.configuration -> 'model' ->> 'name' = :modelName") List<Prompt> findByModelName(@Param("modelName") String modelName);
  4. 데이터 정합성
    • JSON 스키마 검증
    • 타입 안전성 확보
    • 버전 관리 전략 수립

JSONB를 선택한 이유와 대안

  1. 요구사항 분석
    • 유연한 스키마 필요
    • 빈번한 구조 변경
    • 복잡한 쿼리 지원 필요
  2. 대안 검토
    • MongoDB: 별도 시스템 운영 부담
    • EAV 모델: 쿼리 복잡도 증가
    • XML: 처리 오버헤드 큼
  3. JSONB 장점
    • PostgreSQL의 트랜잭션 지원
    • 기존 시스템과의 통합 용이
    • 강력한 쿼리 기능
  4. // 스키마 변경 없이 새로운 필드 추가 가능 prompt.getConfiguration().put("newFeature", newValue); promptRepository.save(prompt);

대규모 JSONB 데이터 처리 최적화

  1. 파티셔닝 전략
  2. CREATE TABLE prompt_configs ( id SERIAL, config JSONB, created_at TIMESTAMP ) PARTITION BY RANGE (created_at);
  3. 배치 처리
  4. @Transactional public void batchProcess(List<PromptConfig> configs) { for (List<PromptConfig> batch : Lists.partition(configs, 1000)) { processConfigBatch(batch); } }
  5. 캐싱 전략
    • 자주 접근하는 JSONB 데이터 캐싱
    • 부분 업데이트 시 캐시 무효화
    • 읽기 성능 최적화