当前位置: 首页 > web >正文

OBCP第四章 OceanBase SQL 调优学习笔记:通俗解读与实践指南

OceanBase SQL 调优学习笔记:通俗解读与实践指南

本文基于OceanBase OBCP V3.0培训教材整理,重点解读SQL调优的核心技术点

一、SQL调优核心方法论

1.1 性能问题来源

SQL性能问题
用户SQL写法
代价模型缺陷
统计信息不准确
数据库物理设计
系统负载过高
客户端路由问题

1.2 调优基本流程

-- 示例:查看SQL执行信息
SELECT * FROM gv$sql_audit WHERE sql_text LIKE '%your_sql%';-- 示例:分析执行计划
EXPLAIN SELECT * FROM orders WHERE user_id = 1001;

优化三步走

  1. 定位瓶颈:通过审计表和SQL Trace找耗时操作
  2. 分析计划:使用EXPLAIN查看逻辑执行计划
  3. 实施优化:改写SQL/调整索引/修改连接方式

二、分区表优化实践

2.1 分区类型对比

分区类型适用场景示例
HASH分区用户ID等离散值PARTITION BY HASH(user_id) PARTITIONS 8
RANGE分区时间序列数据PARTITION BY RANGE(gmt_create)
KEY分区非整型字段PARTITION BY KEY(username)
LIST分区枚举类型(如地区)PARTITION BY LIST(region_code)
二级分区多维度数据(用户+时间)PARTITION BY RANGE(gmt_create) SUBPARTITION BY HASH(user_id)

2.2 分区管理操作

-- 增加分区(仅Range分区支持)
ALTER TABLE sales ADD PARTITION (PARTITION p2024 VALUES LESS THAN ('2025-01-01')
);-- 删除分区
ALTER TABLE sales DROP PARTITION p2020;-- 分区裁剪示例(仅扫描特定分区)
SELECT * FROM sales PARTITION(p2023) 
WHERE amount > 10000;

分区裁剪的作用详见:OceanBase 分区裁剪(Partition Pruning)原理解读

2.3 分区使用建议

  1. 热点打散:用HASH分区分散写入压力
  2. 历史归档:用RANGE分区快速删除旧数据
  3. 分区键选择:必须是主键子集,常用在WHERE条件
  4. 分区数量:单表不超过8192分区(此处指mysql租户,Oracle租户单表分区最多65536),单机推荐<3万

三、索引优化策略

3.1 索引创建原则

-索引表与普通数据表一样都是实体表,在数据表进行更新的时候会先更新索引表然后再更新数据表
-索引要全部包含所查询的列:包含的列越全越好,这样可以尽可能的减少回表的行数
-选择具有高选择性、频繁在where 从句中出现、频繁在join关联字段中的字段
-不对函数或表达式中的字段建索引,要么就建函数索引
-创建一个索引时,评估该索引给查询带来的性能优化是否比因其而引起INSERT,UPDATE,DELETE操作的性能下降以及索引占用的空间更要值得
-在常被修改到字段上建索引需要进行评估

-- 创建覆盖索引(避免回表)
CREATE INDEX idx_orders_user ON orders(user_id, amount);-- 创建函数索引
CREATE INDEX idx_name_lower ON users(LOWER(username));

-覆盖索引:需要显式创建,将查询所需的列包含在索引中,避免回表。
-二级索引的默认行为 :在OceanBase中,二级索引(非聚簇索引)默认只包含索引列和主键列
-回表操作:如果查询的列不在二级索引中,数据库需要通过索引找到主键,再根据主键到主索引(聚簇索引)中查找完整数据行,这个过程就是回表。

通过合理设计覆盖索引,可以避免回表操作,提升查询性能。

索引设计黄金法则

  1. 等值条件前置WHERE a=? AND b>? 索引列a在前
  2. 避免索引失效:不在索引列做运算WHERE YEAR(create_time)=2023
  3. 高选择性优先:选择区分度高的列(如用户ID)
  4. 控制索引数量:每个写操作需更新所有索引

3.2 索引选择机制

查询请求
是否有唯一索引?
直接选择唯一索引
Skyline剪枝
代价模型计算
选择代价最低索引

四、局部索引 vs 全局索引

4.1 核心区别

特性局部索引全局索引
分区方式与主表分区一致独立分区规则
唯一约束需包含所有分区键可独立保证唯一性
跨分区查询需扫描所有分区直接定位目标分区
DML性能影响小(同机事务)影响大(跨机分布式事务)
适用场景分区键常出现在WHERE条件需要非分区键的唯一约束

4.2 创建示例

-- 局部索引(自动继承分区)
CREATE INDEX local_idx ON sales(product_id) LOCAL;-- 全局分区索引
CREATE INDEX global_idx ON sales(store_id) GLOBAL 
PARTITION BY HASH(store_id) PARTITIONS 4;

⚠️ 选型建议:高频精准查询用全局索引,范围扫描用局部索引

五、Hint强制优化

Hint举例
强一致/弱一致: /*+READ_CONSISTENCY(STRONG)*/, /*+READ_CONSISTENCY(WEAK)*/
Server端执行语句超时时间:/*+query_timeout(100000000)*/ 单位微秒
强制使用索引:/*+INDEX(表名 索引名) */
采用并行:/*+PARALLEL(N)*/指定语句级别的并发度。

  • 当该hint指定时,会忽略系统变量ob_stmt_parallel_degree的设置
  • ob_stmt_parallel_degree 用于设置查询的并行度,即可以并行运行的任务数。

/*+ leading(table_name_list)*/

  • 指定表的连接顺序
  • 如果发现hint指定的table_name不存在,leading hint失效;
  • 如果发现hint中存在重复table,leading hint失效

更多hint,参考官网OB 文档:Hint

5.1 常用Hint语法

/*+ 强制索引 */
SELECT /*+ INDEX(orders idx_user) */ * 
FROM orders WHERE user_id=1001;/*+ 指定连接顺序 */
SELECT /*+ LEADING(t1 t2) */ * 
FROM t1 JOIN t2 ON t1.id=t2.id;/*+ 弱一致性读 */
SELECT /*+ READ_CONSISTENCY(WEAK) */ balance 
FROM accounts;

5.2 执行计划绑定

-- 创建Outline绑定计划
CREATE OUTLINE fix_plan ON 
SELECT /*+ USE_HASH(t1 t2) */ * FROM t1,t2...
USING HINT 'USE_HASH(t1 t2)';-- 查看Outline生效情况
SELECT * FROM gv$outline 
WHERE outline_name='fix_plan';

六、性能监控技巧

6.1 实时诊断工具

-- 开启SQL Trace
SET ob_enable_trace_log=1;-- 执行待监控SQL
SELECT COUNT(*) FROM large_table;--(示例大表)-- 查看各阶段耗时
SHOW TRACE;
+-------------------+--------------------------------------------+-------+
| Title | KeyValue | Time |
+-------------------+--------------------------------------------+-------+
| query start | trace_id: "[Y3B6C6451982C-3E9627]"; | 0 |
| parse start | stmt: "select count(*) from large_table"; | 99 |
| pc get plan start | | 16 |
| pc get plan end | | 50 |
| resolve start | | 62 |
| resolve end | | 355 |
| transform start | | 105 |
| transform end | | 107 |
| optimizer start | | 3 |
| optimizer end | | 623 |
| CG start | | 1 |
| CG end | | 156 |
| execution start | | 87 |
| execution end | | 28364 |
| query end | | 166 |
+-------------------+--------------------------------------------+-------+
15 rows in set (0.01 sec)

6.2 (g)v$sql_audit关键字段

字段诊断意义
RETRY_CNT>0表示锁冲突或切主
QUEUE_TIME过大表示CPU资源不足
GET_PLAN_TIME很长表示未命中plan cache
EXECUTE_TIME结合等待事件分析慢SQL根因

七、实战调优案例

7.1 电商订单查询优化

原始SQL

SELECT * FROM orders 
WHERE user_id=1001 AND status='PAID'
ORDER BY create_time DESC;

优化措施

  1. 创建覆盖索引:
    CREATE INDEX idx_user_status ON orders(user_id, status, create_time);
    
  2. 使用逆序扫描:
    SELECT /*+ INDEX(orders idx_user_status) */ *
    FROM orders 
    WHERE user_id=1001 AND status='PAID'
    ORDER BY create_time DESC; -- 自动使用逆序索引
    

通过合理运用分区策略、索引优化和Hint控制,OceanBase的SQL性能可提升数倍至数十倍。建议在复杂查询上线前使用EXPLAIN分析执行计划,并通过gv$sql_audit持续监控生产环境SQL性能。

附录:OceanBase 分区类型速查表

📌 一、一级分区类型

分区类型描述语法示例适用场景注意事项
HASH根据分区键的哈希值将数据均匀分布到各个分区。PARTITION BY HASH(c1) PARTITIONS 5;按用户ID等键值均匀分布数据,消除热点查询。分区表达式结果必须为INT类型;不支持向量(如 HASH(c1, c2))。
KEY与HASH类似,但使用内置哈希函数,支持任意类型。PARTITION BY KEY(c1) PARTITIONS 5;
PARTITION BY KEY() PARTITIONS 5; (使用主键)
无需指定具体哈希计算,支持多列和非INT类型。分区键可不指定(默认为主键);不支持表达式;支持多列向量。
RANGE根据分区键的范围划分数据。PARTITION BY RANGE(unix_timestamp(gmt_create)) (...);按时间范围管理数据,便于历史数据归档和范围查询。支持ADD/DROP分区;最后一个分区不建议使用MAXVALUE
RANGE COLUMNS支持多列和非INT类型(如VARCHAR, DATETIME)的范围分区。PARTITION BY RANGE COLUMNS(gmt_create) (...);多列组合范围查询;非INT类型的时间或字符串范围。不支持表达式;支持多列向量。
LIST根据枚举值列表划分分区。PARTITION BY LIST(c1) (PARTITION p0 VALUES IN (1,2,3));按地区、状态等离散值分类存储。分区表达式必须为INT类型;不支持向量。
LIST COLUMNS支持多列和非INT类型的列表分区。PARTITION BY LIST COLUMNS(region) (...);多列组合的离散值分类。不支持表达式;支持多列向量。
生成列分区使用生成列(由其他列计算而得)作为分区键。PARTITION BY KEY(gc_user_id) ... GENERATED ALWAYS AS (SUBSTRING(t_key,1,4)) VIRTUAL;需要对字段处理后作为分区键的场景。分区键为虚拟生成列。

🔗 二、二级分区组合

二级分区先按一级分区键划分,再按二级分区键细分。常见组合方式包括:

一级分区类型二级分区类型示例场景
HASH / KEYRANGE / RANGE COLUMNS先按user_id散列,再按gmt_create时间范围细分。
RANGE / RANGE COLUMNSHASH / KEY先按时间范围划分,再按user_id散列。
LIST / LIST COLUMNSRANGE / RANGE COLUMNS先按地区列表划分,再按时间范围细分。
RANGE / RANGE COLUMNSLIST / LIST COLUMNS先按时间范围划分,再按状态列表细分。
HASH / KEYLIST / LIST COLUMNS先按user_id散列,再按状态列表细分。
LIST / LIST COLUMNSHASH / KEY先按地区列表划分,再按user_id散列。

💡 建议:若需对RANGE分区进行ADD/DROP操作,建议将RANGE分区作为一级分区(如 RANGE + HASH),而非 HASH + RANGE


⚙️ 三、分区管理操作

操作语法示例说明
增加分区ALTER TABLE members ADD PARTITION (PARTITION p3 VALUES LESS THAN(2000));仅RANGE分区支持以APPEND方式在末尾添加分区。
删除分区ALTER TABLE members DROP PARTITION p3;仅RANGE分区支持删除任意一级分区。

✂️ 四、分区裁剪原理

分区类型裁剪条件示例
HASH/LIST等值查询;分区表达式作为整体出现在等值条件中。WHERE c1 + c2 = 1 可裁剪。
RANGE查询条件与分区范围有交集;等值查询可裁剪,非等值范围查询可能无法裁剪。WHERE c1 = 150 可裁剪;WHERE c1 > 110 AND c1 < 150 可能无法裁剪。
二级分区先按一级分区键裁剪,再按二级分区键裁剪,最终取乘积确定物理分区。WHERE (c1=1 OR c1=2) AND (c2>101 AND c2<150) 可确定具体物理分区。

💡 五、使用建议

  1. 分区键必须是主键的子集
  2. 选择分区类型时,考虑业务SQL的条件形态(确保能触发分区裁剪)。
  3. 避免RANGE分区的最后一个分区使用MAXVALUE,以便后续扩展。
  4. 单表分区数上限为8192,单机分区数推荐不超过3万
  5. 若需在非分区键字段上建立唯一约束,应使用全局索引
  6. 写入频繁的表,主键应尽量有序(如时序递增),避免随机值导致写入放大。

📊 六、附录:MySQL vs Oracle模式支持

分区类型MySQL模式Oracle模式
HASH
KEY
RANGE
RANGE COLUMNS
LIST
LIST COLUMNS
组合分区

此速查表基于OceanBase 3.0版本整理,具体语法和支持范围请以官方最新文档为准。

http://www.xdnf.cn/news/18522.html

相关文章:

  • comfyUI背后的一些技术——Checkpoints
  • React:Umi + React + Ant Design Pro的基础上接入Mock数据
  • Unity编辑器相关
  • 基于STM32设计的大棚育苗管理系统(4G+华为云IOT)_265
  • RabbitMQ:技巧汇总
  • 如何用 SolveigMM Video Splitter 从视频中提取 AAC 音频
  • leetcode_238 除自身以外的数组乘积
  • 实践题:智能客服机器人设计
  • 【Dify(v1.x) 核心源码深入解析】prompt 模块
  • centos下安装Nginx(搭建高可用集群)
  • 利用随机森林筛查 “癌症点”
  • yggjs_react使用教程 v0.1.1
  • Excel中运行VB的函数
  • 自然处理语言NLP:One-Hot编码、TF-IDF、词向量、NLP特征输入、EmbeddingLayer实现、word2vec
  • Docker安装elasticsearch以及Kibana、ik分词器
  • Day24 目录遍历、双向链表、栈
  • k8s集合
  • GIS在城乡供水一体化中的应用
  • CT02-20.有效的括号(Java)
  • Flutter 线程模型详解:主线程、异步与 Isolate
  • 机器学习中的两大核心算法:k 均值聚类与集成学习
  • Linux之Ansible自动化运维(二)
  • 分布式集群压测+grafana+influxdb+Prometheus详细步骤
  • 小程序个人信息安全检测技术:从监管视角看加密与传输合规
  • 【StarRocks】-- SQL CTE 语法
  • Ubuntu22.04安装VMware Tools
  • STM32H750 CoreMark跑分测试
  • Chrome/360 浏览器 WebUI 资源底层机制解析:共享资源与专属资源的奥秘
  • Web自动化测试:测试用例流程设计
  • 如何处理项目中棘手的依赖版本冲突问题