본문 바로가기
Database2026년 9월 19일8분 읽기

학습 기반 쿼리 최적화 — 모델이 옵티마이저를 도울 수 있는 지점

YS
김영삼
조회 119
학습 기반 쿼리 최적화 — 모델이 옵티마이저를 도울 수 있는 지점

데이터베이스 옵티마이저는 같은 SQL을 실행하는 수많은 방법 중 하나를 고른다. 선택의 근거는 비용 추정이고, 비용 추정의 근거는 카디널리티 추정(각 단계에서 몇 행이 나올 것인가)이다. 그리고 이 추정은 자주 틀린다.

학습 기반 접근은 이 추정을 통계 모델이 아니라 학습된 모델로 대체하거나 보정하자는 아이디어다. 실제 실행 결과를 학습 데이터로 삼으면, 통계만으로는 잡기 어려운 컬럼 간 상관관계를 반영할 수 있다.

카디널리티 추정이 얼마나 자주 틀리는지는 EXPLAIN ANALYZE를 몇 번만 돌려 봐도 안다. 추정 100행, 실제 84만 행. 이런 차이가 나면 옵티마이저는 인덱스 중첩 루프를 선택하고, 그 루프가 84만 번 돈다. 쿼리 하나가 데이터베이스 전체를 마비시키는 전형적인 경로다.

추정이 틀리는 구조적 이유

원인예시기존 대응
컬럼 간 상관관계도시=서울 AND 구=강남 (독립 가정이 틀림)확장 통계(다변량 통계)
치우친 분포상위 1% 고객이 거래의 절반히스토그램·MCV 목록
함수·표현식 조건WHERE lower(email) = ...표현식 인덱스와 통계
조인 후 분포 변화조인하면서 분포가 달라짐추정 난도가 급상승
파라미터 스니핑바인드 값에 따라 최적 계획이 다름플랜 캐시 정책
통계 노후화대량 적재 직후ANALYZE 실행 빈도
먼저 할 일은 따로 있다 학습 기반 기법을 검토하기 전에, 통계가 최신인지와 확장 통계로 해결되는 문제인지부터 확인해야 한다. 실무에서 만나는 추정 오류의 상당수는 ANALYZE 한 번이나 다변량 통계 정의로 해결된다.
-- 추정 vs 실제를 먼저 본다
EXPLAIN (ANALYZE, BUFFERS, SETTINGS)
SELECT ... ;
--  rows=100 (estimated)  actual rows=840000  ← 8400배 오차

-- 상관관계가 원인이라면 확장 통계로 개선되는 경우가 많다
CREATE STATISTICS st_city_district (dependencies, ndistinct)
  ON city, district FROM addresses;
ANALYZE addresses;

-- 특정 컬럼의 통계 정밀도를 올린다
ALTER TABLE orders ALTER COLUMN customer_id SET STATISTICS 1000;
ANALYZE orders;

학습 기반 접근의 갈래

1
카디널리티 추정 대체
조건과 테이블 특성을 입력으로 행 수를 예측하는 모델을 학습시킨다. 옵티마이저의 나머지는 그대로 둔다.
2
플랜 선택 보조
옵티마이저가 만든 후보 계획 중 어느 것이 실제로 빠를지 예측한다. 계획 생성은 기존 방식을 쓴다.
3
힌트 제안
과거 실행 이력을 학습해 특정 쿼리 패턴에 적합한 힌트를 제안한다. 사람이 검토해 적용한다.
4
전면 대체
계획 탐색 자체를 학습 모델이 수행한다. 연구 영역에 가깝고 운영 리스크가 크다.

실무 도입 가능성은 위에서 아래로 갈수록 낮아진다. 가장 현실적인 건 2번과 3번이다. 기존 옵티마이저를 그대로 두고 보조하는 방식이라 실패해도 되돌리기 쉽다.

왜 이 문제가 학습에 적합한가

유리한 조건
  • 정답이 명확하다 — 실제 행 수와 실제 실행 시간이 측정된다
  • 학습 데이터가 자동 생성된다 — 매 쿼리 실행이 샘플
  • 평가가 객관적이다 — 더 빠르면 이긴 것
  • 실패가 즉시 드러난다
어려운 조건
  • 데이터 분포가 계속 변한다 — 모델이 낡는다
  • 워크로드가 바뀌면 학습 분포와 어긋난다
  • 최악의 경우가 중요하다 — 평균 개선보다 회귀 방지가 우선
  • 추론 비용이 계획 수립 시간에 더해진다

세 번째 항목이 실무에서 결정적이다. 평균 20% 빨라지는 대신 1%의 쿼리가 100배 느려진다면, 그 시스템은 도입할 수 없다. 데이터베이스는 평균이 아니라 최악의 경우로 평가받는다.

도입을 검토한다면

사전 점검
기존 수단(통계 갱신·확장 통계·인덱스 재설계)으로 해결되지 않는 문제인가
문제가 되는 쿼리 패턴이 반복적이고 안정적인가 (일회성 애드혹은 학습 효과가 적다)
계획 수립 시간이 늘어나도 괜찮은 워크로드인가 (초단위 OLTP에서는 부담)
회귀를 감지할 수 있는 모니터링이 있는가 — 쿼리별 지연 분포 추적
되돌릴 수 있는가 — 기능을 끄면 즉시 기존 동작으로 돌아오는가
데이터 분포 변화 시 재학습 주기와 담당자가 정해져 있는가
-- 도입 전후 비교를 위한 기준선 수집 (pg_stat_statements 활용)
SELECT queryid,
       calls,
       round(mean_exec_time::numeric, 2)  AS mean_ms,
       round(stddev_exec_time::numeric,2) AS stddev_ms,
       round(max_exec_time::numeric, 2)   AS max_ms,
       rows / GREATEST(calls,1)           AS avg_rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 30;

-- 핵심: mean 뿐 아니라 max 와 stddev 를 함께 본다.
-- 평균이 좋아져도 최악이 나빠지면 도입 실패다.

현실적인 대안 — 사람이 하는 학습

모델을 들이지 않아도 같은 원리를 쓸 수 있다. 실행 이력을 모아 문제 패턴을 찾고, 그 패턴에 맞는 구조적 해법을 적용하는 것이다. 사실 이게 튜닝의 본질이다.

  1. 느린 쿼리를 수집한다(pg_stat_statements, 슬로 쿼리 로그).
  2. 추정과 실제의 괴리가 큰 것부터 본다 — 대개 상위 몇 개가 부하의 대부분을 차지한다.
  3. 원인을 분류한다: 통계 문제인가, 인덱스 문제인가, 쿼리 작성 문제인가.
  4. 구조로 해결한다 — 인덱스 추가, 쿼리 재작성, 비정규화, 집계 테이블.
  5. 해결한 패턴을 문서화한다. 같은 패턴이 다시 나타난다.
가장 흔한 해결책 실무에서 추정 오류로 인한 최악의 계획은 조인 순서에서 나온다. 조인 대상을 미리 줄이는 서브쿼리나 CTE로 쿼리를 재작성하면, 옵티마이저가 틀릴 여지 자체가 줄어든다. 모델보다 훨씬 싸고 확실한 방법이다.

전망

학습 기반 최적화는 연구에서 제품으로 넘어오는 중이다. 클라우드 데이터베이스 서비스가 이 기능을 관리형으로 제공하기 시작하면, 개별 팀이 모델을 운영하는 부담 없이 혜택을 볼 수 있다. 그때까지는 기존 도구를 제대로 쓰는 것이 여전히 최선이다. 통계를 갱신하고, 확장 통계를 정의하고, 실행계획을 읽을 줄 아는 것.

자주 묻는 질문

카디널리티 추정이 왜 그렇게 자주 틀리나요?

옵티마이저는 조건들이 서로 독립이라고 가정하는 경우가 많은데 실제 데이터에는 상관관계가 있기 때문입니다. 여기에 치우친 분포, 함수 조건, 조인 후 분포 변화가 더해지면 오차가 수천 배까지 벌어질 수 있습니다.

학습 기반 최적화는 무엇을 대체하나요?

가장 현실적인 형태는 카디널리티 추정을 보정하거나, 옵티마이저가 만든 후보 계획 중 실제로 빠를 것을 예측하는 보조 역할입니다. 계획 탐색 전체를 대체하는 방식은 아직 운영 리스크가 큽니다.

평균 성능이 좋아지면 도입해도 되나요?

아닙니다. 데이터베이스는 최악의 경우로 평가받습니다. 평균이 개선돼도 일부 쿼리가 크게 느려지면 장애로 이어질 수 있으므로, 최대 실행 시간과 분산을 함께 확인하고 즉시 되돌릴 수 있는 스위치를 준비해야 합니다.

먼저 시도해 볼 기존 수단은 무엇인가요?

통계 갱신(ANALYZE), 다변량 확장 통계 정의, 특정 컬럼의 통계 정밀도 상향, 표현식 인덱스, 그리고 쿼리 재작성입니다. 실무에서 만나는 추정 오류의 상당수는 이 단계에서 해결됩니다.

실행계획 문제를 어떻게 찾나요?

EXPLAIN ANALYZE에서 추정 행 수와 실제 행 수의 차이를 보세요. 차이가 큰 노드가 문제의 근원이며, 그 지점에서 잘못된 조인 방식이나 인덱스 선택이 결정됩니다. pg_stat_statements로 부하가 큰 쿼리부터 접근하면 효율적입니다.

조인 순서 문제는 어떻게 완화하나요?

조인 전에 대상을 줄이는 방향으로 쿼리를 재작성하는 것이 효과적입니다. 선택도가 높은 조건을 먼저 적용해 중간 결과를 작게 만들면 옵티마이저가 틀릴 여지가 줄어들고, 실행계획도 안정적으로 유지됩니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록