OceanBase SQL 引擎高级技术学习笔记
如不好理解可以看本人另一篇文章:
Oceanbase SQL 引擎高级技术学习笔记(通俗篇)
一、SQL 请求执行流程
1. 快速参数化(Fast-parser)
仅使用词法分析对文本串直接参数化,获取参数化后的文本及常量参数。
- OBProxy路由决策
- 快速参数化:常量变变量(
@1
,@2
)
2. 词法/语法解析(Parser)
在收到用户发送的 SQL 请求串后,Parser 会将字符串分成一个个的"单词",并根据预先设定好的语法规则解析整个请求,将 SQL请求字符串转换成带有语法结构信息的内存数据结构,称为语法树(Syntax Tree)。
- 将SQL字符串拆分为单词流
- 生成语法树(Syntax Tree)
场景:查询学生成绩
-- 原始SQL
SELECT name, score
FROM students
WHERE class = '3班' AND score > 90;
========================================================
通俗理解:把客户的请求翻译为厨房认识的结构
给餐厅打电话点餐: “要 1 份宫保鸡丁 和 2 碗米饭”
厨师记录:
{
"宫保鸡丁": { "数量": 1 },
"米饭": { "数量": 2 }
}
3. 语义解析(Resolver)
- 将语法树转换为语句树(Statement Tree)
- 识别数据库对象(表、列、索引等)
- 校验SQL语义正确性
============================================================
通俗理解:验证请求的合理性
厨师思考:
✅ 宫保鸡丁在菜单吗?
✅ 米饭是否可点?
❌ 想要"佛跳墙"但菜单没有 → 报错
抱歉,您点的"佛跳墙"不在菜单中
(错误代码 1146:Table 'test.佛跳墙' doesn't exist)
技术要点:
检查表/列是否存在
验证权限:用户能否访问该表?
数据类型检查:不能把日期存到数字列
4. 逻辑改写(Transformer)
通俗理解:用更高效的方式实现相同结果
原需求:“做宫保鸡丁(要鸡肉)”
优化后:“用鸡胸肉代替整鸡(更快出餐)”
==============================================================
- 基于规则的改写:
- 视图合并
- 子查询展开
- 外连接消除
- LIMIT下压
- 基于代价的改写:
- OR-Expansion(或展开)
5 优化器(Optimizer)
通俗理解:选择最佳执行策略
厨师决策:
👨🍳 方案A:用现成鸡胸肉(5分钟)
👩🍳 方案B:现切整鸡(15分钟)
→ 选择方案A
=========================================================
- 生成最佳执行计划
- 核心决策:
- 访问路径选择(索引/全表扫描)
- 连接顺序
- 连接算法(Nested Loop/Hash/Merge Join)
- 分布式计划并行优化
6. 代码生成器(Code Generator)
- 将逻辑计划转换为可执行代码
- 无优化决策,仅忠实翻译
通俗理解: 把优化方案转为可执行指令—>即:生成标准菜谱:
取300g鸡胸肉
油温180℃爆炒
加宫保酱汁翻炒
技术要点:
生成物理算子:IndexScan/HashJoin
确定并行度:开几个灶台同时做
7. 执行器(Executor)
- 本地执行:从顶端算子逐层调用
- 分布式执行:
- 拆分执行树为多个Job
- 通过RPC分发到相关节点
通俗理解: 执行操作并返回结果–>即:厨师按菜谱操作:
单桌订单:自己完成
10桌宴席:分给帮厨并行做
8. 执行计划缓存(Plan Cache)
- 避免重复优化,加速OLTP场景
- 缓存键:参数化后的SQL文本
-- 查看计划缓存统计
SELECT * FROM v$plan_cache_plan_stat;
任务:
通俗理解: 避免重复优化–>餐厅记录:
订单A:宫保鸡丁+米饭 → 方案X(缓存在厨师脑子)
新订单:“同样来一份” → 直接按方案X做
管理机制:
场景 | 处理方式 |
---|---|
相同SQL再现 |
直接复用计划 |
菜单更新(Schema变更) |
刷新缓存 |
内存不足(厨师脑容量不够) |
淘汰最少使用的计划 (最近没人点的菜) |
-- 手动清除缓存(厨师忘记菜谱)
ALTER SYSTEM FLUSH PLAN CACHE;
二、DML语句处理
1. INSERT执行计划
EXPLAIN INSERT INTO t1 VALUES(1,1),(2,2);
ID|OPERATOR |NAME |EST.ROWS
----------------------------------
0 | INSERT | |10
1 | EXPRESSION| |10
2. UPDATE/DELETE执行计划
- 优化器选择访问路径(索引/主表)
-- UPDATE使用索引扫描
EXPLAIN UPDATE t1 SET b=10 WHERE b=1;
ID|OPERATOR |NAME |EST.ROWS
------------------------------------
0 | UPDATE | |11
1 | TABLE SCAN |t1(idx1) |36
3. 一致性校验
- NOT NULL约束检查
- UNIQUE KEY约束检查
- 数据类型自动转换
4. 锁管理
- 行级锁:无表锁,在线DDL不阻塞DML
- 加锁顺序:
- 数据表主键
- 本地索引
- 全局索引
- 热点行优化:
SELECT ... FOR UPDATE; -- 显式加锁
三、DDL语句处理
流程特点
- 零阻塞:DDL不锁表,与DML并行
- 全局一致性:RootServer统一调度
- 自动记录格式:DML适配新Schema
四、查询改写
查询改写(query rewrite):把一个 SQL 改写成另外一个更加容易优化的 SQL。
1. 基于规则的改写
改写类型 | 作用 |
---|---|
视图合并 | 消除视图层,增加连接顺序选择 |
子查询展开 | 转换为Semi/Anti-Join或内连接 |
外连接消除 | 转换为内连接,优化连接顺序 |
LIMIT下压 | 提前过滤减少处理量 |
子查询展开示例
-- 原始SQL
SELECT * FROM t1 WHERE t1.c1 IN (SELECT t2.c1 FROM t2);
-- 改写为内连接(因t2.c1唯一)
SELECT t1.* FROM t1, t2 WHERE t1.c1 = t2.c1;
2. 基于代价的改写(OR-Expansion)
OceanBase 目前只一种支持基于代价的查询改写 - 或展开(Or-Expansion)(考点)
场景1:多索引加速
-- 原始SQL
SELECT * FROM t1 WHERE a=1 OR b=1;
-- 改写后
SELECT * FROM t1 WHERE a=1
UNION ALL
SELECT * FROM t1 WHERE b=1 AND LNNVL(a=1);
场景2:避免笛卡尔积
-- 原始SQL(强制Nested Loop Join)
SELECT * FROM t1,t2 WHERE t1.a=t2.a OR t1.b=t2.b;
-- 改写为Hash Join
SELECT * FROM t1,t2 WHERE t1.a=t2.a
UNION ALL
SELECT * FROM t1,t2
WHERE t1.b=t2.b AND LNNVL(t1.a=t2.a);
五、执行计划分析
OceanBase 的优化器在生成连接顺序时主要考虑左深树的连接形式(考点)
通过Explain命令查看优化器针对给定SQL生成的逻辑
执行计划(考点)
1. EXPLAIN 输出解读
EXPLAIN SELECT * FROM t1,t2 WHERE t1.c1=t2.c1;
ID|OPERATOR |NAME |EST.ROWS
----------------------------------
0 | HASH JOIN | |1980
1 |─TABLE SCAN |t1 |1000
2 |─TABLE SCAN |t2 |1000
- 算子(OPERATOR ) 类型:
TABLE SCAN
:表访问HASH JOIN
:哈希连接SORT
:排序LIMIT
:结果截取
2. 实时执行计划获取
-- 步骤1:查询plan_id
SELECT plan_id FROM v$plan_cache_plan_stat
WHERE statement LIKE 'SELECT * FROM t%';
-- 步骤2:查看物理执行计划
SELECT * FROM v$plan_cache_plan_explain
WHERE tenant_id=1001 AND plan_id=7; --当前版本,必须同时给定tenant_id和plan_id的值,否则系统将返回空集
3. SQL Trace 分析
通过SQL Trace查看执行过程信息及各阶段的耗时
SET ob_enable_trace_log=1; -- 开启Trace
SELECT COUNT(*) FROM __all_table; -- 执行SQL
SHOW TRACE; -- 查看各阶段耗时
六、执行计划缓存管理
1. 淘汰策略
参数 | 作用 | 默认值 |
---|---|---|
ob_plan_cache_percentage |
计划缓存占租户内存比例 | 10(%) |
ob_plan_cache_evict_high |
触发淘汰的内存阈值 | 90(%) |
ob_plan_cache_evict_low |
停止淘汰的内存阈值 | 50(%) |
2. 手动操作
-- 清空当前节点计划缓存
ALTER SYSTEM FLUSH PLAN CACHE;
-- 清空指定租户全局缓存
ALTER SYSTEM FLUSH PLAN CACHE tenant='tenant1' GLOBAL;
3. 使用控制
-- 会话级禁用计划缓存
SET ob_enable_plan_cache = FALSE;
-- Hint强制不使用缓存
SELECT /*+ USE_PLAN_CACHE(NONE) */ * FROM t1;
最佳实践总结
OLTP优化
- 避免复杂子查询,优先使用JOIN
- 利用计划缓存减少解析开销
索引设计
- 为高频过滤条件创建索引
- 组合索引遵循最左前缀原则(考点)
执行计划分析
-- 逻辑计划分析 EXPLAIN EXTENDED SELECT * FROM t1 WHERE c1=100; -- 物理计划验证 SELECT * FROM v$plan_cache_plan_explain WHERE ...;
缓存管理
-- 监控缓存命中率 SELECT hit_count/(hit_count+slow_count) AS hit_ratio FROM v$plan_cache_plan_stat;