数据库 JIT 是什么

发布时间:2026/6/28 2:00:14
数据库 JIT 是什么 数据库 JIT 是什么数据库里的 JIT 通常指在 SQL 查询执行时把查询计划中的某些表达式、过滤条件、投影、聚合、表结构访问等动态生成并编译成更专用的机器码或 JVM 字节码/代码。普通数据库执行大致是SQL → 解析 → 优化器生成执行计划 → 执行器解释执行带 JIT 的执行可能变成SQL → 执行计划 → 为部分计划/表达式生成专用代码 → 编译 → 执行机器码例如SELECT sum(price * quantity) FROM orders WHERE status PAID AND amount 100;JIT 可能会把status PAID、amount 100、price * quantity这类表达式编译成更直接的代码而不是每一行都通过通用表达式解释器判断。作用是什么核心作用是减少解释执行开销提高 CPU 密集型查询的执行速度。主要收益有这些减少函数调用和解释器分发开销通用执行器需要不断判断“这个表达式是什么类型、调用哪个函数、下一步做什么”。JIT 可以把这些逻辑提前固化成专用代码。表达式内联和优化PostgreSQL 的 JIT 可以加速表达式求值和 tuple deforming也就是把磁盘/行格式中的 tuple 转换成内存表示还可以做小函数内联和 LLVM 优化。(PostgreSQL)适合长时间、CPU-bound、分析型查询JIT 本身有编译成本所以短查询可能得不偿失。PostgreSQL 文档明确说JIT 主要对长时间运行的 CPU 密集型查询有利常见是分析型查询短查询的编译开销可能超过节省的执行时间。(PostgreSQL)对重复查询形状更有价值SingleStore 这类系统会把相同 query shape 的查询编译/缓存起来后续相同形状的请求可以复用已生成的计划和代码。(docs.singlestore.com)但 JIT 也有代价编译要耗时、占内存、实现复杂而且不是所有 SQL 算子都适合 JIT。比如 QuestDB 文档就列出其 JIT SQL compiler 对 CPU 架构、AVX2、函数类型、过滤条件等有支持限制。(QuestDB)哪些数据库/SQL 引擎使用了 JIT 或类似代码生成数据库 / 引擎JIT / 代码生成情况PostgreSQL内置 LLVM JIT 支持可加速表达式求值、tuple deforming、内联和优化是否启用由查询成本阈值控制。(PostgreSQL)ClickHouse官方有 “JIT in ClickHouse” 介绍使用 LLVM 基础设施做 JIT用于提升查询执行中的部分计算性能。(ClickHouse)SingleStore / MemSQL使用 code generation将查询形状转换为系统内部 IR 并生成高效机器码后续相同形状查询可复用编译结果。(docs.singlestore.com)QuestDB有 SQL JIT compiler主要针对包含WHERE过滤条件的查询文档说明 6.3 之后默认开启。(QuestDB)Apache Spark SQL不一定叫数据库 JIT但 Spark SQL 使用 code generation 来加速查询Whole-stage codegen 会生成 Java 代码再由 JVM JIT 优化执行。Spark 官方也说明 Spark SQL 包含 cost-based optimizer、columnar storage 和 code generation。(spark.apache.org)SQL Server不是通用 SQL 查询 JIT但 In-Memory OLTP 支持 memory-optimized tables 和 natively compiled stored procedures生成 DLL提高访问和执行效率。(Microsoft Learn)DuckDB典型情况下不走 SQL JIT而是选择向量化解释执行。DuckDB 论文说明它因可移植性和 LLVM 等大型依赖问题选择 vectorized interpreted execution 而不是 SQL JIT。一句话总结数据库 JIT 是把“通用执行器解释 SQL”的一部分变成“为当前查询专门生成并执行机器码”。它适合复杂、长时间、CPU 计算重的分析查询对短小 OLTP 查询不一定有利。回到顶部Bytecode和JIT的关系Bytecode字节码和JIT即时编译不是同一层东西。一句话Bytecode 是一种中间代码格式JIT 是一种把代码在运行时编译成机器码的技术。1. Bytecode 是什么Bytecode 是介于源代码和机器码之间的中间表示。例如 Javaint x a b;编译后不是直接变成 x86 / ARM 机器码而是先变成 JVM 能理解的 bytecodeiload_1 iload_2 iadd istore_3Bytecode 本身通常不能直接被 CPU 执行需要由虚拟机解释执行或者进一步编译。常见例子系统BytecodeJavaJVM bytecodePythonCPython bytecode.NETCIL / MSILSQLiteVDBE bytecodePostgreSQL表达式执行中也会有类似中间表示但不一定叫 bytecode2. JIT 是什么JIT 是Just-In-Time Compilation即时编译。它的作用是程序运行时把某些代码编译成 CPU 可以直接执行的机器码。比如 JVM 执行 Java bytecode 时开始可能解释执行JVM bytecode → JVM 解释器执行如果发现某段代码很热、执行很多次JIT 会把它编译成机器码JVM bytecode → JIT 编译 → x86 / ARM 机器码之后 CPU 就可以直接跑这段机器码速度通常更快。3. 二者核心区别对比项BytecodeJIT本质中间代码格式运行时编译技术是“什么”一种代码表示一种执行优化方式什么时候产生通常在编译前期或解释器准备阶段程序运行时谁执行虚拟机解释器或 JIT 编译后由 CPU 执行JIT 编译器负责编译CPU 执行结果机器码是否一定更快不一定热代码通常更快但有编译开销典型例子Java bytecode、Python bytecode、SQLite VDBE bytecodeJVM JIT、V8 JIT、PostgreSQL LLVM JIT4. 它们的关系Bytecode 和 JIT 经常一起出现但不是必须绑定。常见路径是源代码 ↓ Bytecode ↓ 解释执行或者源代码 ↓ Bytecode ↓ JIT 编译 ↓ 机器码 ↓ CPU 执行所以可以理解为Bytecode 是 JIT 的输入之一JIT 的输出通常是机器码。但 JIT 不一定必须从 bytecode 开始它也可以从 AST、IR、查询计划、LLVM IR 等中间表示开始。5. 放到数据库里怎么理解以数据库执行 SQL 为例Bytecode 型数据库执行比如 SQLite 会把 SQL 编译成自己的 VDBE bytecodeSELECT name FROM users WHERE age 18;大致变成OpenRead Rewind Column Gt ResultRow Next Halt然后由SQLite 的虚拟机一条一条解释执行这些 bytecode。JIT 型数据库执行PostgreSQL、ClickHouse、SingleStore 等系统可能会把查询中的表达式、过滤条件、聚合逻辑等编译成本机机器码WHERE age 18可能被生成成专门的机器码函数而不是每一行都解释执行表达式。6. 一个类比可以这样理解Bytecode 像菜谱的标准步骤。它不是食物本身也不是厨师直接的肌肉动作而是一套中间指令。解释器像厨师每次照着菜谱一步步做。JIT 像厨师发现这道菜要做一万遍于是专门训练出一套最快动作流程。以后再做同一道菜就不用每次慢慢查菜谱了。总结Bytecode 是“代码长什么样”JIT 是“代码怎么被加速执行”。更准确地说Bytecode 中间表示 Interpreter 解释执行 bytecode JIT 运行时把 bytecode/IR 编译成机器码 Machine code CPU 真正执行的代码回到顶部向量化执行Vectorized Execution和JIT的关系向量化执行Vectorized Execution和JIT都是数据库执行引擎的加速技术但它们解决问题的方式不同。一句话向量化执行是“批量解释执行”JIT 是“生成专用机器码执行”。1. 普通解释执行的问题传统 Volcano / iterator 执行模型通常是一行一行处理for each row: 取一行 判断 WHERE 条件 计算表达式 输出结果问题是每处理一行都可能产生大量函数调用、分支判断、虚函数调用、类型判断CPU 利用率不高。2. 向量化执行是什么向量化执行不是一行一行处理而是一批一批处理。例如一次处理 1024 行for each batch of 1024 rows: 批量读取 age 列 批量判断 age 18 批量计算表达式 批量输出结果它的核心思想是把数据库操作作用在一列或一批数据上而不是单行数据上。例如SELECT price * quantity FROM orders WHERE amount 100;向量化执行会更像这样amount[0..1023] 100 price[0..1023] * quantity[0..1023]这样有几个好处优点解释减少函数调用开销一次处理一批数据而不是每行调用一次更适合 CPU cache批量、连续访问列数据更适合 SIMDCPU 可以一条指令处理多个值分支预测更友好减少逐行判断带来的分支开销实现相对简单比 JIT 更容易控制和调试常见采用向量化执行的系统包括DuckDB、ClickHouse、Vectorwise、SQL Server Batch Mode、Snowflake/Velox 类执行框架等。3. JIT 是什么JIT 是在运行时为当前查询生成专门代码。例如这个条件WHERE amount 100 AND status PAID普通解释执行可能每一行都调用通用表达式解释器。JIT 会生成类似这样的专用函数bool filter(row) { return row.amount 100 row.status PAID; }然后编译成本机机器码执行。它的核心思想是不是优化“批量处理方式”而是减少解释器开销生成当前查询专用代码。4. 二者的关系它们不是互斥关系而是两种不同层次的优化。可以这样理解解释执行 一行一行用通用解释器执行 向量化执行 一批一批用通用向量算子执行 JIT 执行 为当前查询生成专用机器码执行 向量化 JIT 一批一批处理同时某些表达式/算子由 JIT 生成专用代码5. 核心区别对比项向量化执行JIT优化对象数据处理粒度代码执行方式核心思想一批数据一起处理为查询生成专用代码输入单位batch / vector / column chunk查询计划、表达式、IR、bytecode输出批量执行结果机器码或可执行函数是否需要编译通常不需要需要运行时编译启动开销较低有编译开销更适合OLAP、扫描、聚合、列式处理长查询、复杂表达式、CPU 密集型计算短查询表现通常也不错可能因编译成本不划算实现复杂度中到高高6. 用一个例子对比SQLSELECT sum(price * quantity) FROM orders WHERE status PAID;行式解释执行读一行 解释 status PAID 解释 price * quantity 累加 sum 读下一行特点简单但每行开销大。向量化执行读一批 status 批量生成过滤 mask 读一批 price、quantity 批量计算 price * quantity 批量累加 sum特点减少逐行开销适合列式和 OLAP。JIT 执行根据这个 SQL 生成专门的过滤和计算函数 编译成机器码 执行机器码特点表达式执行很快但前面要付编译成本。向量化 JIT每次处理一批数据 但过滤和表达式计算由 JIT 生成的专用代码完成这是两者结合的形式。7. 谁更好没有绝对更好取决于场景。向量化执行通常更稳。它没有明显的编译开销适合大量扫描、过滤、聚合、连接等分析型任务。JIT 在复杂表达式、长查询、重复执行的 query shape 上可能更强。但短查询可能还没跑多久JIT 编译成本就已经超过收益了。所以很多现代数据库会选择短查询解释执行或向量化执行 中大型 OLAP 查询向量化执行 复杂 CPU-bound 查询JIT 高性能系统向量化 JIT / codegen8. 一个直观类比向量化执行像工厂流水线一次处理一批零件效率来自批量化和缓存友好。JIT像为某个订单定制一台专用机器机器造出来之后跑得很快但造机器本身有成本。结论