Page 268 - 《软件学报》2026年第3期
P. 268
袁巩生 等: JIT 编译技术在可插拔存储引擎数据库中的应用 1231
MySQL 中, 表达式通过由多个 C++类 Item 的实例组成的 Item 树来表示, 每个节点都表示表达式的不同组成部分,
如操作符、常量值和字段等. 在该模块中, 首先会遍历 Item 树, 并结合优化器中的统计信息评估各个谓词的选择
率. 只有当谓词的选择率低于设定的阈值时, 才会将该谓词加入下推条件中. 此外, 还需要判断 Item 节点是否属于
支持的操作符、字段或常量类型. 如果当前节点尚未支持, 则后续采用混合执行方式, 即同时使用编译执行和解释
执行, 以确保查询的正确性. 对于符合下推条件的谓词, 模块会构造一个新的 Item 树并构建一个包含下推条件字
段编号、条件数量等信息的模板 (template). 该模板与机器码一起下推至存储引擎, 以确保存储引擎能够正确识别
并执行下推条件. 对于不符合下推条件的谓词, 模块会重新构造一个剩余条件的 Item 树, 确保查询在服务层的后
续处理能够保持完整性和准确性.
3.3 表达式编译模块
MySQLJ 的实现目标是将查询中的谓词表达式转换为高效的机器码, 使得生成的机器码比现有系统生成的指
令更少, 并且生成的机器码能够在同一进程中执行. MySQLJ 会将每个 Item 树转换为一个独立的 LLVM IR 模块,
并利用 LLVM 的优化模块进行后续处理. 然后即时编译为本地机器码, 动态链接到当前进程中.
3.3.1 JIT 编译的时机选择
MySQLJ 采用了一种类似于“插件”的非侵入式方法在 MySQL 中实现 JIT 编译系统, 这种方法不会对原有的
Volcano 模型有任何影响, 遵循现有的过滤操作实现逻辑.
如前所述, MySQL 的解析器将输入查询的谓词表达式转化为一个或多个 Item 组成的表达式树, 这些表达式
树作为表达式的内部表示存储在查询块中. 在查询优化过程中, 优化器会根据每个执行路径的代价估算来选择最
优执行路径. 当查询计划确定后, 执行路径会被转换为具体的存取访问迭代器. 这些迭代器负责执行实际操作, 如
表扫描、索引扫描和排序等. 对于谓词表达式, 它会被转换为过滤迭代器. 过滤迭代器会在执行过程中, 通过调用
Item::val_int 评估每一行数据是否满足条件. 在查询优化器最终确定执行计划并生成执行路径之后, 应用 JIT 编译
生成相应的代码. 此时, 所有优化工作已经完成, 查询的执行路径已经固定, 表达式树不再发生变化, 避免了重复编
译. 因此, JIT 编译的操作放在访问路径选择之后, 且在创建迭代器时刻进行编译. 通过这种方式, JIT 编译的机器
码可以与迭代器一起被使用. 在查询执行时, 存取访问迭代器将直接使用这些编译后的代码来判断每一行是否满
足谓词表达式的条件, 从而减少了传统解释执行的开销.
3.3.2 代码生成
我们为每种 Item 类型实现了相应的 CodeGen 方法, 用于生成对应的 LLVM IR. 例如, 对应 Item_int, 系统会调
用 LLVM C++ API getInt64 生成表示 64 位整数的 LLVM IR; 对应 Item_and, 系统会调用相应的 LLVM C++ API
CreateAnd 生成处理逻辑与操作的 LLVM IR. 大部分操作符、字段和常量类型都实现了各自的 CodeGen 方法. 在
调用 CodeGen 时, 每个 Item 会根据其类型生成相应的 LLVM IR, 其子节点递归地调用 CodeGen, 最终生成整个函
数体的 LLVM IR. 通过这种方式, 实现了为整棵查询执行树根据其节点类型动态生成 LLVM IR.
为了更清楚地解释代码生成机制, 这里给出一个 WHERE 语句的条件表达式“cond: (a < 1 AND b > 2) OR c >= 3”作
为例子演示代码生成步骤. 如图 3 所示, 该条件表达式 cond 按照树形结构组织, 代码生成按照自底向上的方式进
行. 每一个算术表达式和过滤条件 e 都被视为一个单元, 编译到单独一个函数 f. 在触发评估表达式的时候, 只需调
用一次相应的函数 f 即可. 首先, 转化逻辑将谓词表达式 p 1 看作一个编译单元 e 1 , 将其编译成字节码<p 1 (%a)>. 根
据字节码的执行结果 %p 1 , 程序利用分支跳转指令 br 跳转到分支 %l 0 或者 %l 2 . 按照逻辑 AND 的语义, 程序可能
直接跳转 %l 2 省略对谓词 p 2 的字节码<p 2 (%b)>执行. 在分支 %l 0 , 编译单元 e 2 被编译成字节码<p 2 (%b)>. 此时逻
辑函数 AND 的语义构建完成. 注意, 从逻辑函数 OR 的角度来看, 逻辑 AND 也是一个转换单元, 记为 e′. 依据转换
单元 e′的字节码执行结果 %p 2 , 程序跳转到分支 %l 1 或者分支 %l 2 , 接着生成占位符Ⓣ与Ⓕ的代码. 按照逻辑 OR
的语义, 占位符Ⓣ直接生成返回 true 的指令, 而占位符Ⓕ则需要评估谓词 p 3 的字节码<p 3 (%c)>的值, 然后返回相
应的结果. 至此, 逻辑函数 OR 最后一个参数谓词 p 3 字节码构建完成, 那么逻辑函数 OR 的代码生成完成, 条件表
达式 cond 的代码生成工作同时也结束.

