[PostgreSQL] PostgreSQL JSON 쿼리 성능 개선과 Java에서의 활용

2025. 4. 6. 00:43·개발 (Development)/PostgreSQL

대용량 JSON 데이터를 PostgreSQL에서 조회하면서 분석 지표를 만들던 중, 생각보다 쿼리 속도가 느리고 결과가 기대와 다르게 나오는 문제가 있었습니다. 특히 JSON 필드 안의 특정 값들을 기준으로 통계 데이터를 구하고, 그 결과를 Java + MyBatis 환경에서 리스트 형태로 받아야 했기 때문에, 정렬이나 형식까지 모두 신경 써야 했습니다.

문제 상황

초기에 작성한 쿼리는 다음과 같은 형태였습니다:

WITH filtered_data AS (
    SELECT
        t."timestamp",
        (t."json_field" ->> 'target_metric')::double precision AS value
    FROM
        "data_table" t
    WHERE
        t."json_field" ->> 'target_metric' IS NOT NULL
        AND t."timestamp" BETWEEN '2025-03-30T15:00:00Z' AND '2025-03-31T15:00:00Z'
)
SELECT
    array_agg("timestamp"::text) AS series,
    MAX(value) AS max,
    MIN(value) AS min,
    AVG(value) AS value
FROM
    filtered_data
GROUP BY
    "timestamp"
ORDER BY
    "timestamp";

이 쿼리는 결과적으로 시간별로 그룹화된 여러 row를 반환했으며, JSON 필드를 반복적으로 파싱하고 있었고, series에는 시간값만 담겨 있어 Java에서 원하는 형태로 데이터를 처리하기 까다로웠습니다.

원하는 결과

  1. 결과는 단 한 줄(row) 만 반환
  2. series 컬럼에는 [timestamp, value] 쌍의 리스트가 JSON 배열 형태로 포함되어야 함
  3. max, min, value는 각각 전체 데이터의 최댓값, 최솟값, 평균값
  4. JSON 배열은 시간 기준 오름차순으로 정렬되어 있어야 함
  5. Java에서는 List<List<Object>> 또는 커스텀 DTO로 쉽게 파싱 가능해야 함
  6. 특정 필드(target_metric) 에는 0 과 100 사이의 값만 필터링 (협업 팀 이슈사항)

성능 개선과 쿼리 리팩토링

기존 쿼리의 중복 파싱, 불필요한 GROUP BY, 정렬 누락 등을 해결하기 위해 다음과 같이 리팩토링했습니다:

WITH filtered_data AS (
    SELECT
        t."timestamp",
        (t."json_field" ->> 'target_metric')::double precision AS value
    FROM
        "data_table" t
    WHERE
        t."json_field" ->> 'target_metric' IS NOT NULL
        AND t."timestamp" BETWEEN '2025-03-30T15:00:00Z' AND '2025-03-31T15:00:00Z'
        AND (
            (
                LOWER(t."json_field" ->> 'target_metric') LIKE '%metric%'
                AND (t."json_field" ->> 'target_metric')::double precision BETWEEN 0 AND 100
            )
            OR LOWER(t."json_field" ->> 'target_metric') NOT LIKE '%metric%'
        )
)
SELECT
    json_agg(json_build_array(
        f."timestamp"::text,
        f.value
    ) ORDER BY f."timestamp") AS series,
    MAX(f.value) AS max,
    MIN(f.value) AS min,
    AVG(f.value) AS value
FROM
    filtered_data f;

쿼리 개선 포인트

  • JSON 필드 파싱을 한 번만 수행하고, 필터링과 변환을 CTE에서 처리해 효율화
  • json_agg(... ORDER BY ...) 구문을 사용하여 JSON 배열 내부 순서 보장
  • 최종 결과는 단일 row 로 반환되며, Java에서 바로 사용 가능한 형태 제공

Java에서 받는 형태 예시

{
  "series": [
    ["2025-03-30T15:01:00", 82.4],
    ["2025-03-30T15:02:00", 83.1],
    ...
  ],
  "max": 88.4,
  "min": 80.5,
  "value": 84.26
}

Java DTO 예시

public class MetricStats {
    private List<List<Object>> series;
    private Double max;
    private Double min;
    private Double value;
}

혹은 더 명확하게 사용하고 싶다면:

public class TimestampValuePair {
    private String timestamp;
    private Double value;
}

public class MetricStats {
    private List<TimestampValuePair> series;
    private Double max;
    private Double min;
    private Double value;
}

PGobject 에러 해결 방법

PostgreSQL의 json_agg 결과는 Java에서 PGobject로 넘어올 수 있기 때문에, 아래 중 하나를 적용해 에러 없이 처리할 수 있습니다.

  1. 쿼리에서 ::text 캐스팅 사용
  2. 커스텀 TypeHandler 등록
  3. DTO에서 PGobject로 받고 직접 Jackson 등으로 파싱

가장 쉬운 방법은 쿼리에서 ::text로 강제 문자열 캐스팅하는 것입니다:

json_agg(...)::text AS series

마무리

PostgreSQL의 JSON 처리 기능은 유연하지만, 성능과 실사용 측면에서 신경 써야 할 부분이 많습니다. 위와 같은 방식으로 쿼리를 정리하고, Java와 연동하기 편한 형태로 가공하면 실시간 데이터 처리나 지표 시각화에서도 성능과 생산성을 동시에 잡을 수 있습니다.

데이터가 많아질수록 이런 최적화를 점점 더 고려하는 방향으로 진행해야 한다는 점을 느꼈습니다. 지금까지는 느리고 다루기 어려웠던 JSON 기반 쿼리도, 이제는 빠르고 명확하게 처리할 수 있게 되었습니다.

반응형

'개발 (Development) > PostgreSQL' 카테고리의 다른 글

[PostgreSQL] 시퀀스 자동 증가 설정과 오류 해결 방법 정리  (0) 2025.05.18
[PostgreSQL] 실전 활용 - 문자열 포함, 쉼표 구분 배열 매칭, JSON 배열 필터링  (0) 2025.04.26
[PostgreSQL/TimescaleDB] TimescaleDB 사용 중 hypertable 및 chunk 포함 전체 백업과 복원하기  (0) 2025.04.05
[PostgreSQL] MyBatis에서 PostgreSQL JSON 데이터 처리하기  (1) 2025.03.30
[PostgreSQL] 최댓값 구할 때 Max vs Order by  (0) 2025.03.22
'개발 (Development)/PostgreSQL' 카테고리의 다른 글
  • [PostgreSQL] 시퀀스 자동 증가 설정과 오류 해결 방법 정리
  • [PostgreSQL] 실전 활용 - 문자열 포함, 쉼표 구분 배열 매칭, JSON 배열 필터링
  • [PostgreSQL/TimescaleDB] TimescaleDB 사용 중 hypertable 및 chunk 포함 전체 백업과 복원하기
  • [PostgreSQL] MyBatis에서 PostgreSQL JSON 데이터 처리하기
LoopThinker
LoopThinker
모르는 것을 알아가고, 아는 것을 더 깊게 파고드는 공간
  • LoopThinker
    CodeMemoir
    LoopThinker
  • 전체
    오늘
    어제
    • 분류 전체보기 (184) N
      • 개발 (Development) (120) N
        • Algorithm (1)
        • Angular (1)
        • AWS (4)
        • DeepSeek (2)
        • Docker (6)
        • Git (3)
        • Java (20) N
        • JavaScript (4)
        • Kafka (4)
        • Kubernetes (2)
        • Linux (6)
        • PostgreSQL (33) N
        • Python (17)
        • React (3)
        • TypeScript (3)
        • Vue.js (5)
        • General (6)
      • 데이터 분석 (Data Analysis) (1)
      • 알고리즘 문제 풀이 (Problem Solving.. (27)
      • 자격증 (Certifications) (24)
        • ADsP (14)
        • 정보처리기사 (4)
        • Linux Master (5)
        • SQLD (1)
      • 기술 동향 (Tech Trends) (10)
      • 기타 (Others) (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Kubernetes
    JSON
    Linux
    python
    DevOps
    ADsP
    typescript
    리눅스 마스터 2급 2차
    java
    Vue.js
    pandas
    백준
    AWS
    springboot
    MyBatis
    백준자바
    PostgreSQL
    Kafka
    자바
    Linux master
    백준온라인저지
    리눅스 마스터
    javascript
    데이터분석
    오답노트
    deepseek
    Vue
    백준알고리즘
    docker
    리눅스 마스터 2급
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
LoopThinker
[PostgreSQL] PostgreSQL JSON 쿼리 성능 개선과 Java에서의 활용
상단으로

티스토리툴바