OBCP第三章 OceanBase SQL 引擎高级技术学习笔记

发布于:2025-06-27 ⋅ 阅读:(19) ⋅ 点赞:(0)

OceanBase SQL 引擎高级技术学习笔记

如不好理解可以看本人另一篇文章:
Oceanbase SQL 引擎高级技术学习笔记(通俗篇)

一、SQL 请求执行流程

缓存命中
缓存未命中
添加计划
SQL请求
Fast-parser
Plan Cache
Executor 执行器
Parser 词法/语法解析
Resolver 语义解析
Transformer 逻辑改写
Optimizer 优化器
Code Generator 代码生成
返回结果

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;
原始SQL文本
词法分析
Tokens:
SELECT, name, 逗号, score,
FROM, students,
WHERE, class, 等号, '3班',
AND, score, 大于号, 90
语法分析
语法树
SelectStmt
列列表
name
score
FROM students
WHERE条件
AND连接
class='3班'
score>90

========================================================

通俗理解:把客户的请求翻译为厨房认识的结构
给餐厅打电话点餐: “要 1 份宫保鸡丁 和 2 碗米饭”

厨师记录:

{
  "宫保鸡丁": { "数量": 1 },
  "米饭": { "数量": 2 }
}

3. 语义解析(Resolver)

  • 将语法树转换为语句树(Statement Tree)
  • 识别数据库对象(表、列、索引等)
  • 校验SQL语义正确性

============================================================

通俗理解:验证请求的合理性
厨师思考:
✅ 宫保鸡丁在菜单吗?
✅ 米饭是否可点?
❌ 想要"佛跳墙"但菜单没有 → 报错

抱歉,您点的"佛跳墙"不在菜单中
(错误代码 1146Table '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
  • 加锁顺序
    1. 数据表主键
    2. 本地索引
    3. 全局索引
  • 热点行优化
    SELECT ... FOR UPDATE; -- 显式加锁
    

三、DDL语句处理

流程特点

Client OBServer RootServer 所有节点 DDL请求 发送Schema变更 持久化Schema 异步刷新Schema Client OBServer RootServer 所有节点
  • 零阻塞:DDL不锁表,与DML并行
  • 全局一致性:RootServer统一调度
  • 自动记录格式:DML适配新Schema
    DDL

四、查询改写

查询改写(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;

最佳实践总结

  1. OLTP优化

    • 避免复杂子查询,优先使用JOIN
    • 利用计划缓存减少解析开销
  2. 索引设计

    • 为高频过滤条件创建索引
    • 组合索引遵循最左前缀原则(考点)
  3. 执行计划分析

    -- 逻辑计划分析
    EXPLAIN EXTENDED SELECT * FROM t1 WHERE c1=100;
    
    -- 物理计划验证
    SELECT * FROM v$plan_cache_plan_explain WHERE ...;
    
  4. 缓存管理

    -- 监控缓存命中率
    SELECT hit_count/(hit_count+slow_count) AS hit_ratio 
    FROM v$plan_cache_plan_stat;
    

网站公告

今日签到

点亮在社区的每一天
去签到