Training a 4B model to produce 81% faster query plans than Postgres
Postgres보다 81% 빠른 쿼리 플랜을 만들어내는 4B 모델 훈련하기
저자 Rohan Bansal이 4B 파라미터 오픈 웨이트 모델을 SFT와 에이전틱 RL로 사후 학습시켜, pg_hint_plan 힌트를 통해 Postgres 기본 플랜보다 빠른 쿼리 플랜을 생성하게 한 실험을 상세히 기록한 글입니다. 처음에는 113개 조인 쿼리 중 99개에서 플랜조차 만들지 못하던 모델이 학습 후 Join Order Benchmark 전체에서 평균 44.7%의 지연 시간 감소를 달성했습니다.
- 주제
본문
Leis et al.은 2015년에 '쿼리 옵티마이저는 과연 얼마나 좋은가?'라는 질문을 던졌고, 10년 뒤 같은 질문을 다시 던졌습니다. 그 사이 쌓인 방대한 연구에도 결론은 옵티마이저가 여전히 많은 것을 남겨두고 있다는 것이었습니다. 저자는 여기서 출발합니다. 쿼리 옵티마이저의 핵심 작업인 조인 순서 결정(join ordering)은 NP-hard로 알려져 있지만, 선택된 플랜이 좋은지 검증하는 것은 어렵지 않습니다. 실행 시간이라는 단일 축만 최적화하면 되므로, 검증 가능한 출력을 학습하는 데 강한 언어 모델과 강화학습에 문제가 깔끔하게 환원된다는 것이 이 실험의 핵심 아이디어입니다.
왜 쿼리 옵티마이저는 어려운가
글은 IMDb 데이터셋의 일부(title 약 100만 행, movie_companies 약 200만 행, company_name 약 10만 행 등)를 예로 들어 설명합니다. '2000년대에 가장 많은 타이틀을 낸 일본 회사는?'이라는 질의에서 WHERE 절의 선택 조건(selective predicate)이 붙는 순간, 어떤 테이블을 먼저 조인하느냐에 따라 후속 조인에 넘기는 행 수가 10만 행과 40만 행으로 4배까지 차이가 납니다. 여기에 각 조인이 쓸 수 있는 Hash·Merge·Nested-loop 조인, outer/inner 방향, 그리고 Sequential·Index·Index-only·Bitmap 스캔까지 조합하면 이 단순한 쿼리 하나에도 4,608가지 실행 방법이 생깁니다. Postgres는 동적 계획법(12개 이상 조인에는 유전 알고리즘)으로 탐색 공간을 줄이지만, 조인이 하나 늘 때마다 공간은 조합적으로 폭발합니다.
더 근본적인 문제는 Postgres가 계획 단계에서 실제 카디널리티를 셀 수 없다는 점입니다. 그 대신 pg_statistic의 최빈값·빈도·히스토그램으로 추정하고, 조인 간 분포는 '균등 분포 가정'으로 넘깁니다. 이 가정이 틀리면 크게 틀리는데, 예컨대 일본 회사가 5%인데 전체 영화의 50%를 만들었다면 첫 조인 결과가 10만 행이 아니라 100만 행이 됩니다. 초기 조인의 잘못된 추정 하나가 조인 트리 전체의 추정을 연쇄적으로 오염시킵니다.
pg_hint_plan과 문제 정의
Postgres는 항상 비용이 가장 낮은 플랜을 고르고 비용 모델은 소스 수정 없이 바꿀 수 없으므로, 저자는 서드파티 확장 pg_hint_plan을 사용합니다. SQL 위에 구조화된 힌트 주석을 붙여 조인 알고리즘이나 스캔 방식을 강제할 수 있습니다. 그래서 문제는 '언어 모델이 더 좋은 플랜으로 이어지는 힌트를 생성하도록 학습할 수 있는가'로 재정의됩니다.
중요한 판단이 하나 있습니다. 저자는 모델에게 Postgres 플래너와 같은 정보를 줘서 더 나은 카디널리티 추정기를 만드는 방향은 수십 년의 연구와 싸우게 되고 추론 지연도 감당이 안 된다고 보고 버립니다. 대신 무거운 분석 워크로드처럼 같은 쿼리가 수천 번 반복 실행되는 패턴을 겨냥합니다. 학습 과정에서 한 쿼리를 수십~수백 번 실행하는 비용이 들어도, 이후 모든 실행에 걸쳐 상각되므로 일회성 쿼리의 Pareto 효율이 아니라 반복 쿼리에서 Postgres를 이길 수 있다는 계산입니다.
모델, 에이전트 하네스, 벤치마크
모델은 독일 Empero가 공개한 empero-ai/Qwen3.8-4B-Distill로, Qwen 3.8의 2.4T 모델을 교사로 Qwen 3.5 4B에 증류한 것입니다(MMLU는 개선, GSM8K는 소폭 하락). 이 모델을 감싸는 경량 에이전트 qo-agent는 inspect_relation, get_column_stats, get_plan, evaluate_candidate, keep_default, finish의 6개 도구를 갖고, 에이전트는 PlanAction JSON 객체를 생성하면 evaluate_candidate가 이를 힌트로 컴파일해 원래 쿼리 앞에 붙여 실행·계측합니다.
벤치마크는 두 개입니다. 검증용으로 Leis et al.의 Join Order Benchmark(JOB, IMDb 기반 113개 쿼리·33개 템플릿), 학습용으로 약 13.6k 개 합성 쿼리·16개 템플릿의 Cardinality Estimation Benchmark(CEB)를 씁니다. JOB과 구조가 같은 CEB 쿼리로 과적합하지 않도록, 쿼리의 '토폴로지'(별칭을 제거한 조인 그래프)를 추출해 겹침을 검사했고 실제 겹침은 없었다고 합니다. 같은 IMDb로 학습·평가하는 것은 오히려 의도된 것으로, 기업의 특정 데이터베이스에서 반복 돌아가는 분석 워크로드를 잘 배우는 것이 목표이기 때문입니다.
측정 노이즈와의 싸움
이 실험의 절반은 측정 공학입니다. 학습 신호 전체가 '후보 플랜이 기본 플랜보다 빠른가'의 측정에 의존하기 때문입니다. 저자는 Postgres 컨테이너에 각각 코어 4개·메모리 8GB를 고정 배정하고, shared_buffers를 128MB로 둔 뒤 워밍업 단계에서 EXPLAIN (ANALYZE, TIMING OFF, BUFFERS)의 shared hit blocks·read blocks 카운터가 직전 실행 대비 2% 이내로 안정되고 플랜이 바뀌지 않을 때 'warm'으로 선언합니다(최소 2회, 최대 5회). 그런데도 job-13b 쿼리는 20회 측정 중 14회가 186~204ms, 6회가 227~253ms로 두 개의 속도 덩어리를 보였습니다. 평가는 후보·기본을 교차로 3쌍씩 실행해 중앙값 비율로 비교하고 차이가 5% 미만이면 동점 처리하는데, 이 구조에서는 느린 덩어리에 3개 중 2개 이상이 떨어질 확률이 약 20%라 실제 차이가 없는 플랜에도 14~26%의 유령 향상·악화 신호가 학습에 주입될 수 있음을 계산으로 보여줍니다. 변동계수(CV)가 아니라 이 '속임 비율(fooling rate)' 자체를 최소화해야 한다는 분석으로 이어집니다.
결과와 학습 구성
하이라이트로 정리된 결과는 이렇습니다. 113개 조인 중심 쿼리에서 44.7% 지연 감소(기본 플랜 대비 약 1.8배, 제목의 '81% 빠른 플랜'). 본질적으로 노이즈가 큰 환경에서 롤아웃을 채점하는 커스텀 GRPO 변형 설계. 학습은 빌린 2x H100 노드에서 vLLM과 트레이너를 돌리고 Postgres 컨테이너 4개는 집 책상 위 머신에서 돌리는 2대 분산 구성. 그리고 약 500개의 GPT-6 Astra 에이전트 궤적에 대한 오프폴리시 증류까지 수행했습니다.
Hacker News 반응
- @fsmv — 하지만 쿼리 플랜이 실제로 쿼리가 요청한 것을 수행하는지는 어떻게 알 수 있을까요?
- @timcobb — 쿼리 플랜에 변환 연산을 적용해서 동치 여부를 판정할 수 있지 않을까요?
- @polyphilz — pg_hint_plan에는 디버그 로그가 있어서 Postgres가 실제로 힌트를 사용했는지 여부를 검증할 수 있습니다! 평가 과정에서 이를 사용했습니다.
- @amluto — pg_hint_plan은 받아들이는 힌트가 반드시 해당 쿼리에 유효한 플랜이 되도록 설계되어 있으리라 생각합니다. 이 속성을 갖추면서 고품질 플랜도 표현할 수 있는 스킴은 가능하고 과도하게 복잡하지도 않습니다. 다만 제안된 알고리즘이 의도한 대로 동작하는지 일반적으로 검증하는 것은 불가능합니다 — 문제를 어떻게 정의하느냐에 따라 결정 불가능하거나 NP-hard 혹은 co-NP-hard가 됩니다.
- @hedgehog — 데이터베이스 배포에 4B 파라미터 모델을 추가하는 일에는 별로 흥분되지 않지만, 앱을 테스트하는 동안 이 접근법으로 Postgres가 성능을 남겨두고 있는 쿼리 플랜을 식별하는 것은 리스크 없이 가치 있어 보입니다.
원문: rohanbansal.com (via Hacker News) / 번역·요약: Trawling