카테고리 없음

PostgreSQL JSONB

DoRightting 2024. 11. 9. 16:54

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 데이터 캐싱
    • 부분 업데이트 시 캐시 무효화
    • 읽기 성능 최적화