SIGMOD 2026
这片文章记录sigmod2026的实验心路。为了防止不必要的麻烦,写一写踩坑、心得,细节只有只言片语。
主要研究内容:用大模型进行索引优化。
prepare
首先比较了启发式算法和学习式算法。
根据各种实验+调研,得出几个结论:
索引性能的优化的效果:启发式>学习式(离线)>学习式(在线)
推荐索引的选择时间,如果不算训练时间,学习式比启发式快很多。
上述比较均是在非常复杂的查询场景下,即多表、嵌套、聚合函数等,DB的snapshot也非常大。在简单的SQL上,因为可供选择的索引组合数量很少,优化空间小,启发式方法表现好,学习式方法没什么意义。
现有学习型方法不能很好的进行查询的嵌入,以及更加meta的信息的提取。现有的encode方法都比较朴素。
综合3.4.,现有学习型方法只在JoinOrderBenchmark(专门用来测试查询优化器的)表现良好,TPCH/TPCDS上有点拉胯。在事务场景(CRUD)下甚至会负优化(维护索引开销大于索引带来的查询优化)。
- 现有数据集严重不足,大模型微调产生的提升太小。
我们需要的:尽可能复杂的查询。同时,将多个查询装载为一个workload,对一个workload(一批查询)推荐一组索引。
大规模数据集的收集,和生成workload-index监督数据,是难点。
model
8.29
我们重新设计了开源LLM的架构,并进行微调。
首先我们进行SFT的测试。不得不说llama factory真是个好用的框架。
参数比较复杂,过几天总结记录一下。
9.4
学长修复了检查点保存的问题。今天进行训练和测试的熟悉。
因为有现有的模型,首先进行SFT。
preliminary:lmf hook
见hookSystem篇。
nohup
nohup 命令可以把程序挂在后台。
记得做好输出。
MMConfig
--mm:多模态布尔开关。
--mm-efs:指定嵌入文件路径。
--mm-flm:冻结语言模型布尔开关。
--mm-pt:训练多模态布尔开关。
--mm-pckpt:多模态投影层的检查点路径。
SFT
SFT必须开启多模态。冻结语言模型,训练投影层。
投影层是个两层神经网络(大约1m参数),用于将768维列嵌入映射到3584维语言模型空间。
我进行SFT训练的配置。
torchrun --nporc_pernode=1 script/train.py useless.workload config/sft-projector.yaml \
--mm \
--mm-embedding-files data/* #这里是多模态表示列嵌入的文件,格式为safetensor,safetensor下面解析。\
--mm-projector-trainable \
--mm-flm #freeze language model \
--sft #这条在sft的配置文件中写了。但我觉得不使用llama-factory可能会解析不到。 \
每个checkpoint保存的内容的复用:
可以直接在--mm-pckpt中复用model的safetensor。
safetensor
safetensor在本项目中是使用TAPAS/TaBert通过嵌入要做推荐的表格生成出来的。
结构是:键(数据库.表.列):768维嵌入向量。也就是每一列有一个768维嵌入。
RLHF
多模态交叉实验时,batch_size和梯度累计都开1依然oom……实验室算力紧张,算鸟。
SWIRL
继续做上游适配。
9.8
今天的任务,修改workload接口。
现有workload_generator的接口:
Class Workload:
queries
budget
description
其中,queries是Query对象的list.
从Query到workload中有workload_from_tuple(tuples)的中间过程,workload其实是在这里生成的。
其中tuples是包含两个元组(查询类tuple,频率分布tuple)的列表或者集合。
这个函数通过组合query_class,query_text,freq生成workload.
要输入的workload类型中的key:
- “id”(如果没有则自增,最后的查找可能比较麻烦?根据id转换回query内容。),
- “query”查询本身
- “frequency”频率
其他的信息不解析。
根据config中的workload size作为步长,在json中采样。
对齐大模型输入。
TODO:
- 继续数据准备:26个数据集上的索引。单个查询对应的最优索引,无论如何先作为标签推荐出来,跑通大模型。
- 大模型还依赖:数据分布信息,依赖编码操作。与数据独立。在PRICE上做embedding。
- 调整细节问题:prompt。
- 多查询mm微调。
一,prompt处理。二,标签(index)。
9.9
要弄清楚的问题:
SWIRL的存储消耗是怎么计算的?
对齐自己输入的workload与SWIRL中的大小。
已经对齐了SWIRL的输入端。
在config中增加了两个参数:
ExternalWorkload:布尔类型,决定是否使用外部workload.
WorkloadPath:外部workload的路径。
现在要考虑的对齐:
- workload的size。之前的链路是,有查询类,然后config中固定workload大小,按大小采样Query。现在如果固定workload size,因为外部输入的workload size是不固定的。
- 要么用config中的workload size,这样,就先洗混workload中的Query,按输入的size采样。
- 要么将外部输入,直接按json的格式拿出来。
- label标签的添加。
- 每个workload一个label.
9.10
SWIRL的workload嵌入问题依然没解决。因为外部输入的workload大小不超过15,那么直接将feature表征宽度都按15来,不足的补0可不可行。
同时对PRICE中的表格数据用TAPAS进行嵌入。
执行column_embedding的脚本:
python scripts/gen/column_embedding/{dbname}.py --db {dbname} --output /data/baiyutao/PRICEembedding/{dbname}.safetensor --sample_count 100
在此之前,我们需要数据库的schema。已经有另一个脚本,支持直接从pg的数据库得出。
python scripts/gen/schemas.py --add-init --db [dbname]
9.11
虽然论文中说SWIRL可以通过补0或者语义压缩等方法实现workload大小的对齐,但是代码中确实没有实现这一点。
今天的todo:
- SWIRL实现训练workload的对齐。
- index eab用启发式算法做多查询workload上的index标签生成。
- 针对多查询的workload上进行查询级别的嵌入。
- SWIRL的padding实现
一般来说是用动态填充和注意力掩码实现。
尝试了一下,性能估计器会出问题。
- indexEAB启发式打标签
跑通了。命令:python heu_run.py --res_save /index_advisor_selector/index_selection/heu_res.json \ --process --overhead \ --sel_params parameters \ --exp_conf_file /configuration_loader/index_advisor/heu_run_conf/extend_config.json \ --constraint storage --budget_MB 3000 \ --cand_gen permutation --est_model optimizer \ --work_file /workload_generator/template_based/tpch_work_temp_multi.json\ --db_conf_file /configuration_loader/database/db_con.conf\ --schema_file /configuration_loader/database/schema_tpch.json\
9.12
今天生成PRICE中的多query workload标签,并嵌入到自己的大模型里。
好多。顺便验证了SWIRL使用生成的workload也能保存到workload+label的标签中。
9.13
正式开始大规模微调。
流程:
用带标签的数据训练投影层,即SFT投影层,可以在多个数据库上联合进行。寻找合适的超参数,确保训练充分,最后的loss值应该降到0.x甚至0.0x,让训练是过拟合的,从而可以在训练的ckpt中找到最优。同时,投影层的训练要求更高的学习率,1e-3是经过验证的不错的学习率,epoch时间尝试长一些,待验证。
CUDA_VISIBLE_DEVICES=2 python scripts/sft_projector.py workloads/index_eab/10q/job+tpch.2000w.5-15q.workload.labeled.json --eval-set-percent 0.1 --output-dir saves/sft_projector_both --mm --mm2 --mm-flm --mm-embedding-files embeddings/imdb.tapas.1000.safetensors embeddings/tpch1g.tapas.10000.safetensors --mm2-embedding-files workloads/index_eab/job_1500.safetensors workloads/index_eab/tpch_2000.safetensors --mm-projector-trainable --mm2-projector-trainable --epochs 30 --batch-size 4 --learning-rate 1e-3 --debug config/sft-projector.yaml > ~/output_qwen_projector_both.txt 2>&1 &联合训练投影层与映射层,要将投影层参数穿进去,进行sft。这里用PRICE数据集跑一下,当作预训练流程,在足够多的数据集上预训练之后,保证在新数据集上有效果而不用进一步微调。
CUDA_VISIBLE_DEVICES=3 python scripts/sft_projector.py workloads/index_eab/10q/job+tpch.2000w.5-15q.workload.labeled.json --eval-set-percent 0.1 --output-dir saves/sft_full --mm --mm2 --mm-embedding-files embeddings/imdb.tapas.1000.safetensors embeddings/tpch1g.tapas.10000.safetensors --mm2-embedding-files workloads/index_eab/job_1500.safetensors workloads/index_eab/tpch_2000.safetensors --mm-projector-trainable --mm2-projector-trainable --mm-pckpt saves/sft_projector_both/checkpoint-2000/projector.safetensors --mm2-pckpt saves/sft_projector_both/checkpoint-2000/projector.safetensors --epochs 6 --batch-size 4 --learning-rate 1e-4 --debug config/lib-sft.yaml > ~/output_full_sft0.txt 2>&1 &
- eval阶段,PRICE上训完之后,看看在TPCH上的效果。效果不好的话:多用几个数据集/变化查询数量和复杂度。
python scripts/eval.py workloads/index_eab/10q/tpch.2000.1000w.5-15q.workload.json eval/res1.json --mm --mm2 --mm-embedding-files embeddings/tpch.tapas.1000.safetensors --mm2-embedding-files workloads/index_eab/tpch_2000.safetensors --ckpt 2300:2400:100 --gpu 2 -b 4 --db tpch1g --dbuser wuyinjun --dbpswd "" --debug config/eval-qwen.yaml
- 后文。
提示词调整:现在的prompt还有荣誉,列向量中不应该放进去所有列,而是应该收集关键列,比如join/predicate中的。我们要从workload中收集关键列。
对列向量本身的嵌入做改进,现在是单个sample100,依照多模态的经验,可以像卷积神经网络那样,尝试多次采样。即列嵌入时生成多个embedding,多次采样。
TODO
首先,我要从PRICE中生成:多批,每批1000个workload,每个大小为10,frequency先固定,用extend或者SWIRL打标签。
尝试用SWIRL打标签:训练8k个,打500个,慢成史了,估算了一下要跑80h,紧急停止了。
尝试用extend:首先要改成IndexEAB能接受的数据,然后做一些偏移。IndexEAB一开始的三种workload:1.template:用模板,只改参数;2.perturb:修改predicate的值,添加额外列。3.random:随机生成select-proj-join查询。
9.14
又整理了一下pipeline:
PRICE中的初始SQL负载: 准备3个数据集上的测试一下:Baseball,Basketball,chembl。
每个各1k个workload,workload大小均为10个。
先修改一下SWIRL的逻辑,使得生成结果可以直接对齐sft.这一步与indexEAB无关。(已完成)
->index_advisor中的脚本script/gen/workloads.py,add_labels.py,merge.py可以将sql生成sft可用的workload文件。同时,这里的sql要进行embedding.
workload.py直接提取出基础workload结构
add_labels为每个workload加上labels,这里要用到index_eab的推荐结果
merge.py将多个workload文件合并成一个
这里labels的生成,要用extend.
SWIRL的生成方法:直接生成出sft可用的格式。
extend则需上面的脚本。
尝试用extend生成。首先要把PRICE数据集分割成id+sql+freq的形式以供算法使用。
先把freq都设为1。
几个重要的参数:
--cand_gen
->进行sft