实验
1. 推理阶段的few-shot
首先考虑一下prompt的构成
如何把shot加入到prompt中?
这里参考一下baseline的方法。
LLM方法中的prompt构成:固定的指令+转化后的demo示例+转化后要处理的workload。对齐为characteristic的格式化标识。
推荐哪些shot?
查看一下index_selection_evaluator是如何适配到LLM baseline的,从而用启发式生成最好的标签。
添加shot具体的构造?
在线prompt的生成阶段。
单独加一段,再以组合embedding的方式放进去,或者格式化一种新的embedding,还是类似baseline拆解出来characteristic,但是受限于context还是要做embed。
尝试了LLM中的few-shot测试。
这里忘记了联合训练时的projector,要求SQL嵌入和column嵌入。也就并不适合用他们的fewshot的prompt结构。
所以尝试修改我们的在线prompt构建阶段以进行few-shot。分阶段进行。
参考shot的生成。
这里打算直接复用LLM的baseline中获取启发式方法结果的接口。
单独开一种数据格式专门用于shot?
应该是需要的,实现一个pool用来retrieval.retrieval检索阶段。
首先:考虑LLM那篇baseline的方法,即提取出characteristic的余弦相似度。
或者:直接计算不同workload的embedding相似度,O(N)扫一遍所有嵌入,余弦相似排序取top-k。嵌入阶段。
这里先搞清楚,projector是如何与大模型结合的。
因为projector的特殊,所以对于shot中的SQL也要做embedding,但是index最好直接用自然语言表征。
所以这里的retrieval阶段也必须是不可见的,不能像baseline中显式拆解开直接当作prompt。
也就是对于推理阶段的一个workload:
构造在线prompt时,除了原始的文本转换之外,要做retrieval->先把workload拆开,对齐成pool中表征的形式,从而retrieval->得到retrieval的结果,sql用embedding的形式放入prompt,index用自然语言->构造结束。
1.1移植过程:
这里记录把baseline的方法迁移到我们的模型做few shot。
demo construction阶段
demo 持久化格式
demo余弦相似匹配时,首先会检索metadata,如果命中直接索引到
metadata
相当于缓存余弦匹配信息。
这里不关注细节,characterize和uncharacterize的过程对于prompt的构建是不可见的。
完整demo信息
这里关注"workload_id“:"{benchmark}_workload_{id}"用来索引到初始的子集wl。
以及索引信息的表征:CREATE/DROP INDEX的语句,这里要考虑在哪个阶段把语句转化为最基础的索引形式(table(column,...))?在refine阶段之后就可以了,此时已经准备好了wl与对应的标签,其实只需要返回wl包含的sql和标签,不再需要characteristics,同样也不需要重新执行CREATE/DROP INDEX。这时候用类似微调时的格式包装好,后面在推理的在线prompt生成时,根据sql找到对应的embed,再以自然语言的形式把索引放进去就行了。
构建pipeline
- 输入一个.sql池,并用workload_generation.py分割成随机大小子集wl.
- 启发式生成候选索引:使用
magic_mirror。
输入为当前分割后的子集wl,放入custom_workloads/下面。
原始输出是一些包含“workload/预算/对应索引”(每个算法都生成一遍)的文件。 - 在construct阶段,拆解上面的wl,并找到不同预算下的最佳候选索引,组装成demo。这里要注意cross join。
- 返回时寻找对应embed,这里在线做,即推理过程动态加载。要根据id->sql->md5 hash 找到对应的tensor。这里嵌入的方式复用model中加载sql_embedding的方法。
- 实现上可以把llm的baseline作为子模块,在function.py中加一个wrapper。
prompt构建阶段
10.30的待办:
- demo检索已经完成了,但是demo中的索引格式还不能用。重点关注一下
existed indexes和default used indexes的区别。看看怎么样提取出来能直接返回的索引格式。最好是json格式的table-column映射。 - 返回之后,思考在大模型的哪个阶段导入retriever。目前的想法是,在to_prompt函数,增加一个布尔开关:是否启用few-shot。以决定是否加载retriever类的返回值。这样也可以让后面 few shot sft的情况复用。开关暂时先不暴露,如果后面想加入到运行参数也比较简单。
- 以及考虑如何将tensor填充进去。参考一下之前是直接在占位符的位置载入embed。。
2. 训练阶段的few-shot
3. dsb的差表现
数据库的特征:tapas表格嵌入再看一遍论文。sample_count的scale影响?区分PRICE和几个专用负载数据集。
workload的特征:要不要再做提取?
还是因为context的限制?
4. 用训练后的模型在LLM的baseline上测试
是为了测试zero-shot到few-shot的迁移能力。
这里考虑一下projector对于context的影响,以及sft格式与LLM中在线prompt完全不同的影响。
5.重新打标
意外发现之前生成的监督数据标签质量不够好。重新用RL的方法生成标签看看。
按说是sota的rl方法SWIRL表现也不比启发式好多少。
storage budget问题
导师也考虑到这个问题。所以要实现一个工具,用来给所有label排序,按delta cost/delta storage进行排序。都按虚拟值计算。这个工具要不要内嵌进SWIRL?看一下实现难度。
ob比赛
首先整理了一下架构,仍然等待补全:
这里按从输入SQL到磁盘IO的流程进行总结。
Session
每次Client和Observer连接,都用一个session管理。
这里Session管理的内容:
- Db:当前选中的数据库。
- Trx:唯一事务指针。
- Event:也就是SQL请求的文本包装。
Parser
主要是Lexer和Parser
- Lexer词法分析:把字符流分解成有意义的词法单元。对应
lex_sql.l - Parser语法分析:把词元组合成AST树。对应
yacc_sql.y
生成描述不同种类语句的SqlNode
Resolve
接下来在ExpressionBinder完成语义绑定,在ParseStage/ResolveStage完成语法解析/语义解析。ResolveStage负责把语义绑定到stmt,由stmt面向不同SQL类型的语义对象,封装解析结果。
Optimizer
这一层是从stmt生成不同的query plan(查询计划)并以此找到对应的operator(算子)。
要根据SQL语句不同分成两类。
DDL: 也就是
CREATE/DROP等数据定义语言。执行会跳过optimizer以及plan generator,直接调用到Command Operator,也就不需要调用physical operator。DML:也就是
SELECT/UPDATE等数据操作语言。这里就会先交给optimizer。optimizer内部会进行:Logical plan generation->optimal rewrite->Physical plan generation.在Physical plan生成后,会调用physical Operator执行具体操作。
Storage
存储层的metadata和engine需要额外注意。每次操作都要注意元数据的存取或者更新,同样,最终交互的“契约”(CRUD)是在执行引擎中实现的。
调用链如下:
Default Handler
默认执行器是在session创建时就加载的。
->Db
管理一个Db对象的生命周期。因为这里只需要考虑每次都在同一个数据库中。这里用Db下面的接口实现Db对Table的操作
->Table
管理一个Table的生命周期。DML/DDL需要绑定到对应的Table对象。Table有一个独占指针unique_ptr调用table_engine,在执行引擎中进行record级别的管理。
->Table Engine/Index/Record Manager
表的执行引擎,这里不需要考虑lsm,用heap的就行。
执行引擎中,data page(数据页)的管理会交给bufferpool处理(开关/刷脏/回收/删除等)。
这里在操作时,需要注意table meta和Index meta的管理。
->bufferpool/WAL
这次不需要管bfp的实现,包括lru/刷脏等算法和接口都已经实现好了。
WAL写前日志,等到做到故障恢复再看。
Trx事务
Trx也是在session初始化时就创建的,会一直透传到record级别操作。目前mvcc实现很简陋,实现的时候再记录
Index
Index由TableMeta声明IndexMeta时产生,在table engine中实现,用bfp维护一个对应的磁盘文件。
代码中已经给好了B+树的实现,可以直接用get_entry/delete entry获取操作时的接口。
q1. text类型的实现 (待修复
‘
以及作为LOB对象实现了基础的Create Drop Select.手测成功。
Update等队友合并进去之后实现。
这样避免了分页的问题,但是注意有没有其它问题,比如:
LOB对象一般来说不应该加载到bufferpool_manager中。
同样也禁止有索引。
提测之后崩了,还没debug。目前在做比较重要的UNIQUE。
q2. multi-index (完成
这里其实要把原本的单索引链路都打通成容器,KV形式我在想要不要用map,还是说一个vector直接起名为表名。
难点在于修改B+树构造。
此外,要注意t(c1,c2)和t(c2,c1)是不同的,在B+树中作为KV构造的时候,必须保持列的顺序。
解决了,直接把多个值拼装成B+树的节点,并给这个节点一个单独的rid.
q3.UNIQUE (待修复
这里实现唯一索引后,要和UPDATE对接。又出问题了。
冲突检查都是在执行引擎做的,但是总cover不了样例,果然是做update的队员留下的历史遗留问题。因为想保持代码在mvcc层复用,于是算子上做了重构,做update的时候,index的逻辑不能向后兼容,,,