카테고리 없음
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
);
주요 특징
- 빠른 처리 속도
- 이진 형식 저장으로 재파싱 불필요
- 인덱싱 지원으로 빠른 검색
- 공간 효율성
- 중복 키 제거
- 불필요한 공백 제거
- 강력한 쿼리 기능
- -- 특정 키 검색 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 타입의 차이점
- 저장 방식
- JSON: 입력된 텍스트를 그대로 저장
- JSONB: 파싱된 이진 형식으로 저장
- 성능 특성
- JSON: 저장은 빠르지만 매 쿼리마다 재파싱 필요
- JSONB: 초기 저장은 느리지만 쿼리 처리가 빠름
- 실무적 장점
- 인덱싱을 통한 빠른 검색
- 공간 효율적인 저장
- 중복 키 자동 제거
- -- JSONB는 인덱싱 지원 CREATE INDEX idx_config ON configs USING GIN (data);
JSONB 인덱싱 전략
- GIN 인덱스 활용**
- -- 전체 문서 검색이 필요한 경우 CREATE INDEX idx_full ON configs USING GIN (data); -- 특정 키에 대한 검색이 빈번한 경우 CREATE INDEX idx_specific ON configs USING GIN ((data->'specific_key'));
- 부분 인덱스 활용
- -- 특정 조건의 데이터만 인덱싱 CREATE INDEX idx_partial ON configs USING GIN (data) WHERE (data->>'status') = 'active';
- 성능 모니터링
- 실행 계획 분석
- 인덱스 사용률 추적
- 주기적인 VACUUM ANALYZE 수행
JSONB를 사용할 때의 주의점
- 스키마 관리
- 완전한 스키마리스가 아닌 하이브리드 접근
- 주요 필드는 일반 컬럼으로 관리
- 변동성 높은 데이터만 JSONB로 저장
- 성능 고려사항
- 깊은 중첩 구조 지양
- 자주 검색되는 필드는 일반 컬럼으로 분리
- 적절한 인덱싱 전략 수립
- @Query("SELECT p FROM Prompt p WHERE p.configuration -> 'model' ->> 'name' = :modelName") List<Prompt> findByModelName(@Param("modelName") String modelName);
- 데이터 정합성
- JSON 스키마 검증
- 타입 안전성 확보
- 버전 관리 전략 수립
JSONB를 선택한 이유와 대안
- 요구사항 분석
- 유연한 스키마 필요
- 빈번한 구조 변경
- 복잡한 쿼리 지원 필요
- 대안 검토
- MongoDB: 별도 시스템 운영 부담
- EAV 모델: 쿼리 복잡도 증가
- XML: 처리 오버헤드 큼
- JSONB 장점
- PostgreSQL의 트랜잭션 지원
- 기존 시스템과의 통합 용이
- 강력한 쿼리 기능
- // 스키마 변경 없이 새로운 필드 추가 가능 prompt.getConfiguration().put("newFeature", newValue); promptRepository.save(prompt);
대규모 JSONB 데이터 처리 최적화
- 파티셔닝 전략
- CREATE TABLE prompt_configs ( id SERIAL, config JSONB, created_at TIMESTAMP ) PARTITION BY RANGE (created_at);
- 배치 처리
- @Transactional public void batchProcess(List<PromptConfig> configs) { for (List<PromptConfig> batch : Lists.partition(configs, 1000)) { processConfigBatch(batch); } }
- 캐싱 전략
- 자주 접근하는 JSONB 데이터 캐싱
- 부분 업데이트 시 캐시 무효화
- 읽기 성능 최적화