4B小模型RL优化SQL查询比Postgres快81%:一个$1200的实验

一个开发者用Qwen 4B模型通过SFT+RL两阶段训练,让小模型学会生成比Postgres原生更好的SQL执行计划,113条复杂JOIN查询上达成1.81倍加速。

一个40亿参数的小模型,用强化学习调教后,生成的SQL执行计划比Postgres自己选的快81%。这不是论文空想,是真跑出来的数据。

先说结论

开发者Rohan Bansal用Qwen 3.8 4B蒸馏版,通过两阶段训练——SFT(监督微调)+ Agentic RL(强化学习),让一个小模型学会了给Postgres生成更好的SQL查询计划。

在113条复杂JOIN查询上,最终达成1.81倍几何平均加速,总延迟下降44.7%。多次采样取最优时,零回退。

总训练成本:$1,200。全部代码开源。

Postgres的查询优化器有多难

你以为数据库最了解自己的数据?错了。Postgres做查询优化的核心难题是JOIN顺序选择——这是个NP-hard问题。

举个例子:3张表JOIN,光考虑连接顺序和扫描方式,就有4,608种可能的执行计划。5张表?3,300万种。

Postgres用统计信息估算每个计划的代价,选最低的那个。但估算经常翻车——假设数据均匀分布,实际可能严重偏斜。一个早期JOIN的估算错误,会像多米诺骨牌一样污染整棵计划树。

核心思路:用pg_hint_plan"遥控"Postgres

Postgres不允许你直接改它的代价模型。但有个巧妙的旁路:pg_hint_plan扩展。

只需在SQL语句上方加一行特殊注释,就能强制Postgres用指定的JOIN方式和扫描方式:

1
2
/*+ HashJoin(a b) SeqScan(a) */
EXPLAIN SELECT * FROM t1 a JOIN t2 b ON a.id = b.id;

于是问题变成了:语言模型能不能学会生成比Postgres默认更好的hint?

两阶段训练方案

阶段一:SFT蒸馏(教模型"说话")

模型一开始连工具都不会用。作者先用GPT-6 Astra跑了约500条查询的完整轨迹,然后把这些轨迹蒸馏进4B模型。

只用了100条轨迹(展开后382条训练样本),一个epoch就让模型学会了工具调用格式。两个epoch后开始产出有效计划。

阶段二:Agentic RL(教模型"思考")

SFT只是照猫画虎,RL才让模型真正理解什么是"好计划"。

训练流程:模型对每条查询生成候选hint → 发送到真实Postgres执行 → 测量执行时间 → 用自定义GRPO算法更新权重。

作者还精心设计了奖励函数:用中位数比值而非单次测量来减少噪声,并设置了5%的平局区间。

训练结果:从废铁到神器

检查点有效计划几何加速回退数
原始4B模型14/1130.85x1
SFT完成71/1131.16x5
RL 600步99/1131.35x0
RL 1200步101/1131.41x2
3次采样取最优113/1131.81x0

从完全不会用工具,到比Postgres原生快81%。零回退意味着没有一条查询因为用了AI的计划而变慢。

几个值得注意的技术细节

◆ 噪声控制是关键:作者花了大量精力去抖——4个Postgres容器隔离运行、2GB shared_buffers确保缓存驻留、warmup机制、中位数配对比较。没有这些,RL的奖励信号就是垃圾。

◆ 奖励函数迭代了两版:第一版太严厉(-3惩罚),模型吓得只敢返回默认计划。改成锚定GRPO后才真正学会探索。

◆ SFT的val loss没动≠没在学:第二个epoch验证损失没降,但实际性能提升了。这说明loss不是万能指标。

◆ 适用场景明确:不是对所有SQL都有用。适合重复执行的分析型查询——训练成本摊到成千上万次执行后,每次查询都更快。

给开发者的启示

这个实验证明了一件事:小模型+RL在垂直领域可以打败大模型和传统系统。

关键不是模型有多大,而是你能不能定义清楚"什么是好的"——在这个场景里,执行时间就是唯一的裁判。一旦有了可验证的奖励信号,4B参数就够了。

作者原话:“容易忽视小模型的存在,毕竟5万亿参数的庞然大物让人目眩。但我们不应该。”