暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

崖山数据库性能优化(一):统计信息、执行计划与索引的"三重门"——【YashanDB学习篇--0x06】

Acdante 2026-03-28
58



写在前面

上两篇聊了高可用和备份恢复,今天开始进入一个更硬核的话题——性能优化。以下仅仅是一些个人愚见,性能优化,个人推荐老虎刘,刘大师的课程、书籍以及优化方法论都是一流,有幸看过一些,学了刘大师的一点点皮毛,可关注刘大师的公众号和相关课程。

老虎刘谈性能优化

老虎刘,公众号:老虎刘谈SQL优化126-一次性能故障引申出来的8个SQL优化知识点

性能优化这事儿,说简单也简单,加索引、加内存、加机器,三板斧抡下去,大部分问题都能缓解。说难也难,因为错误的优化比不优化更危险——一个错误的索引可能让写入性能腰斩,一个错误的函数用法可能让索引失效,一个错误的 SQL 逻辑可能让整个系统雪上加霜。

这篇是性能优化系列的第一篇,我们先从最基础、最通用、也最容易被忽视的三件事讲起:

  1. 1.统计信息
     —— 优化器的"眼睛"
  2. 2.执行计划
     —— SQL 的"体检报告"
  3. 3.索引
     —— 最快也最危险的武器

一、统计信息:优化器的"眼睛"

1.1 什么是统计信息?

先打个比方:你去餐厅点菜,服务员会告诉你"这道菜是辣的、那道菜份量大、这道甜点适合两个人吃"——这些信息帮你做决策。

数据库的优化器也一样。它在决定一条 SQL 怎么执行之前,需要知道:这张表有多少行?这个列有多少个不同的值?数据分布是均匀的还是倾斜的?索引的深度和选择性如何?

这些就是统计信息(Statistics)。

优化器根据统计信息来估算每种执行路径的成本,选择它认为"最优"的执行计划。如果统计信息不准确,优化器就会做出错误的判断——就像拿着过期地图导航,越努力越偏。

1.2 YashanDB vs Oracle:统计信息管理对比

对比维度
Oracle
YashanDB
自动收集
默认开启(GATHER_STATS_JOB)
支持自动统计信息收集
手动收集
DBMS_STATS.GATHER_TABLE_STATS
GATHER STATISTICS 语句
收集粒度
表级/列级/Schema级/全库级
表级/列级/Schema级
直方图
支持(频率/高度平衡直方图)
支持列数据分布直方图
过期判断
STALE_PERCENT 阈值(默认10%)
基于数据变更量自动判断
锁定统计信息
DBMS_STATS.LOCK_TABLE_STATS
支持锁定
查看方式
DBA_TAB_STATISTICS DBA_TAB_COL_STATISTICS
系统视图查看

核心差异:Oracle 的统计信息管理经过了二十多年的打磨,DBMS_STATS 包的功能极其丰富。YashanDB 在基础能力上完全对齐,但在高级场景(如增量统计信息收集、统计信息导出/导入等)还在持续完善。

1.3 统计信息为什么这么重要?

再举个真实的例子——

一张订单表有 1000 万行数据,status 列只有 3 个值:'PENDING'、'COMPLETED'、'CANCELLED',但数据分布极不均匀——99% 是 'COMPLETED','PENDING' 只有 1000 条。

业务执行:SELECT * FROM orders WHERE status = 'PENDING';

  • 如果统计信息准确
    :优化器知道 'PENDING' 只有 1000 行,会选择走索引扫描(Index Scan),毫秒级返回
  • 如果统计信息过期
    :优化器可能以为数据均匀分布,估算出 330 万行,选择全表扫描(Full Table Scan),几秒钟才返回

同样的 SQL,不同的统计信息,性能差距可以达到百倍甚至千倍。

1.4 收集统计信息的最佳实践

什么时候该收集?

  • 表数据发生大量变更后(INSERT/UPDATE/DELETE 超过一定比例)
  • 定期收集(如每天凌晨业务低峰期)
  • 建表、重建索引之后
  • 执行计划出现异常时

收集语句示例——Oracle:EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA', 'TABLE_NAME');
;YashanDB:GATHER STATISTICS FOR TABLE schema.table_name;

💡 经验之谈: 很多生产环境的性能问题,根源就是统计信息不准。当你发现一条 SQL 突然变慢,第一件事不是加索引,而是先检查统计信息是否过期


二、执行计划:SQL 的"体检报告"

2.1 执行计划是什么?

当一条 SQL 提交给数据库,优化器会生成一个"执行方案"——先扫描哪张表、用什么方式扫描、表之间怎么关联、数据怎么排序——这就是执行计划(Execution Plan)

读执行计划,本质上就是读懂数据库"打算怎么做这件事"。

2.2 查看执行计划

操作
Oracle
YashanDB
查看执行计划
EXPLAIN PLAN FOR + DBMS_XPLAN.DISPLAY
EXPLAIN PLAN FOR ...
实际执行统计
SET AUTOTRACE ON / SQL*Plus
支持执行统计输出
AWR 历史执行计划
DBA_HIST_SQL_PLAN
支持历史执行信息查询
绑定变量窥探
自动(OPTIMIZER_PEEKED_BINDS)
支持绑定变量感知

YashanDB 查看执行计划示例:EXPLAIN PLAN FOR SELECT * FROM orders WHERE customer_id = 1001;
 然后 SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

2.3 执行计划常见"坑"

① 全表扫描(FULL TABLE SCAN)什么时候合理?

很多人看到全表扫描就慌,急着加索引。但全表扫描不一定是坏事:

  • 表很小(几百行),全表扫描比索引扫描更快
  • 查询需要返回表中大部分数据(>15%-20%),索引回表的成本反而更高
  • 批量处理场景,顺序 I/O 比随机 I/O 更高效

什么时候不合理?大表(百万行以上)做精确查询却走了全表扫描;WHERE 条件有高选择性的列,但没建索引。

② 索引失效的常见原因

这是性能优化中最让人痛心的场景——明明建了索引,优化器就是不用。原因通常是:

失效原因
示例
本质
对索引列使用函数
WHERE UPPER(name) = 'ACDANTE'
索引存的是原值,函数变换后不匹配
隐式类型转换
WHERE varchar_col = 123
数字与字符串比较,索引失效
LIKE 左模糊
WHERE name LIKE '%DB'
无法利用 B-Tree 索引的有序性
NOT IN / !=
WHERE status != 'DELETED'
优化器判断回表成本高,选择全扫
IS NULL(部分场景)
WHERE col IS NULL
取决于NULL值数量和统计信息
统计信息过期
优化器错误估算行数,选择次优计划

③ 连接方式选择错误

YashanDB(和 Oracle)支持多种表连接方式:

连接方式
适用场景
性能特征
Nested Loop
小表驱动大表,有索引可用
适合驱动表行数少的情况
Hash Join
大表等值连接,无可用索引
需要足够的内存存放 Hash 表
Merge Join
两表都有排序的连接列
适合已排序数据,内存需求低

优化器选错连接方式,通常是因为行数估算错误——又是统计信息的锅。


三、索引:最快也最危险的武器

3.1 索引的本质

一句话:索引是用空间换时间的艺术。

没有索引时,找一条数据就像在一本没有目录的字典里查字——只能从头翻到尾(全表扫描)。有了索引,就像有了拼音目录,几毫秒就能定位到目标页。

但索引不是免费的:每次 INSERT/UPDATE/DELETE,都要同步维护索引;索引占用额外的存储空间;过多的索引会增加优化器的选择难度。

3.2 YashanDB vs Oracle:索引能力对比

能力
Oracle
YashanDB
B-Tree 索引
✅(默认)
位图索引
✅(LSC 表场景)
函数索引
唯一索引
联合索引
分区索引
✅(Local/Global)
索引压缩
支持
不可见索引
✅(INVISIBLE)
支持测试不影响执行计划
索引跳跃扫描
12c+
支持

3.3 索引设计的"黄金法则"

法则一:高选择性列优先建索引

选择性 = 不同值数量 / 总行数

  • 身份证号:选择性 ≈ 1(每行都不一样)→ 最适合建索引
  • 性别:选择性 ≈ 0.0001(只有男/女)→ 几乎不适合单独建索引
  • 订单状态:选择性低且数据倾斜 → 需要配合直方图

法则二:联合索引的字段顺序很重要

假设查询模式是 WHERE dept = ? AND hire_date > ?——好的设计是高选择性字段放前面(CREATE INDEX idx_dept_date ON employees (dept_id, hire_date)
),差的设计是低选择性字段放前面(CREATE INDEX idx_date_dept ON employees (hire_date, dept_id)
)。

原则:把过滤最多记录数的字段放在第一位。

法则三:覆盖索引避免回表

如果一个查询需要的所有列都包含在索引中,优化器可以只读索引、不读表——这叫覆盖索引(Covering Index),是性能优化的利器。

例如,查询只需要 customer_id 和 order_date,创建索引 CREATE INDEX idx_cover ON orders (customer_id, order_date)
,那么 SELECT customer_id, order_date FROM orders WHERE customer_id = 1001
 这条 SQL 可以只读索引完成。

法则四:别建太多索引

每多一个索引,INSERT/UPDATE/DELETE 就多一份维护成本。经验法则:OLTP 表单表索引不超过 5-7 个;频繁更新的表索引越少越好;批量导入场景考虑先删索引导入再重建。

3.4 索引的反面教材

错误操作
后果
修复建议
对低选择性列建索引
索引几乎不会被使用,浪费空间
删除或改为位图索引
重复索引(列相同,顺序相同)
双倍维护成本,无额外收益
审查并删除冗余索引
函数索引忘记收集统计信息
优化器无法准确估算,可能选错计划
收集统计信息
大表缺少主键索引
唯一性检查全表扫描
必须建立主键
联合索引顺序错误
索引无法被使用,查询退化为全表扫描
调整列顺序或重建索引

四、信息收集:优化前的"望闻问切"

在动手优化之前,最重要的事情是收集信息。中医讲"望闻问切",性能优化也一样——先诊断,后开药。

4.1 需要收集哪些信息?

信息类别
具体内容
收集方式
业务模型
读写比、并发量、峰值时段、热点表
与业务方沟通 + 应用监控
数据分布
表行数、列基数、NULL比例、数据倾斜度
系统视图查询
统计信息
是否过期、最后收集时间、直方图状态
系统统计视图
等待事件
最耗时的等待类型(IO/锁/CPU)
性能监控视图
TOP SQL
耗时最长、执行次数最多的 SQL
慢 SQL 日志 / 系统视图
执行计划
实际执行路径、行数估算偏差
EXPLAIN
硬件资源
CPU/内存/磁盘IO/网络 使用率
操作系统监控

4.2 YashanDB vs Oracle:性能信息收集工具

工具/能力
Oracle
YashanDB
慢 SQL 日志
alert log + AWR
慢查询日志
等待事件分析
V$SESSION_WAIT/V$SESSION_WAIT/V$SYSTEM_EVENT
性能监控视图
TOP SQL
AWR Top SQL / V$SQL
系统 SQL 统计视图
执行计划分析
DBMS_XPLAN / AWR
EXPLAIN PLAN
基线对比
AWR Baseline
支持性能基线
实时监控
Enterprise Manager (OEM)
命令行 + 系统视图

4.3 优化思路决策树

SQL 慢了? 按以下顺序排查——

第一步:检查统计信息是否过期。如果过期,收集统计信息后重新执行。

第二步:检查执行计划是否异常。全表扫描但表很大→考虑建索引;索引存在但未使用→检查失效原因(函数/隐式转换/数据分布);连接方式错误→检查行数估算,必要时用 Hint 引导;子查询效率低→改写为 JOIN。

第三步:检查 SQL 逻辑是否有问题。SELECT * 包含不必要的列→减少列;缺少分页→加 LIMIT/TOP;循环中调 SQL→改为批量操作;事务过大→拆分事务。

第四步:排除非 SQL 问题。锁等待→检查锁冲突;IO 瓶颈→检查磁盘和缓冲区;内存不足→调整缓冲区参数。


五、通用优化手法速查表

不管用 Oracle 还是 YashanDB,以下优化手法通用且高频

类别
优化手法
效果等级
说明
SQL 层
收集统计信息
⭐⭐⭐⭐⭐
基础中的基础,不收集其他优化都是盲人摸象
SQL 层
避免 SELECT *
⭐⭐⭐
减少网络传输和内存消耗
SQL 层
用绑定变量代替拼接
⭐⭐⭐⭐
减少硬解析,提高软解析命中率
SQL 层
子查询改 JOIN
⭐⭐⭐
很多场景下 JOIN 更高效
SQL 层
避免索引列上使用函数
⭐⭐⭐⭐
函数索引或改写 SQL
索引层
建立合适的选择性索引
⭐⭐⭐⭐⭐
核心优化手段
索引层
联合索引字段顺序优化
⭐⭐⭐⭐
高选择性字段放前面
索引层
利用覆盖索引避免回表
⭐⭐⭐⭐
热点查询效果显著
索引层
审查并删除冗余/无用索引
⭐⭐⭐
减少写入开销
配置层
调整 DATA_BUFFER_SIZE
⭐⭐⭐⭐
减少物理 IO
配置层
调整 VM_BUFFER_SIZE
⭐⭐⭐
减少排序/Hash Join 的磁盘交换
配置层
选择合适的表类型
⭐⭐⭐⭐
HEAP/TAC/LSC 各有适用场景
配置层
合理使用分区表
⭐⭐⭐⭐
分区剪枝减少数据访问量
架构层
连接池化
⭐⭐⭐⭐⭐
避免频繁建连的开销
架构层
批量操作替代循环单条
⭐⭐⭐⭐
减少网络往返和事务开销

六、尾声:人人都可以是全栈

写到这里,想起一件很有意思的事。

十年前,数据库调优是"高级 DBA"的专属领地——你需要懂存储引擎原理、会读 10046 trace、能背出各种隐含参数的作用。那个时候,"会调库"是一种稀缺能力,一种职业壁垒。

十年后的今天,AI 写 SQL 比你快,ChatGPT 分析执行计划比你准,Copilot 自动生成索引建议。甚至,一个刚毕业的前端工程师,借助 AI 工具,也能写出"八九不离十"的数据库查询。

壁垒在坍塌。

不是 DBA 不重要了,而是"只会调 SQL"的 DBA 不重要了。当 AI 能完成 80% 的执行计划分析,剩下的 20%——那些需要理解业务、理解数据分布、理解系统全局的能力——才是真正值钱的。最近又看到不少公司开始让“牛马”用AI和openclaw跑业务流程,跑通即刻下岗,用人跑AI,跑通流程后优化人。进入了一个奇怪的循环,各地都在大力推进一人公司{OPC公司},各种零元入住、各种OPC创业社区涌现,也是一个时代下的缩影,都在快马加鞭往前进。

这让我想到海子的那句诗。改一改,送给这个时代所有的技术人:

《面朝 AI,Token花开》

文:Acdante · 省略号先生

从明天起,

做一个全栈的人 

写前端,写后端,连数据库,

从明天起,

关心 AI 和代码

一个人,搞定Docker,流程,业务,营销和流程

不断优化,不断跑通

用一个个Token,代替人工

用一个个Claw,实现流程

我有一台服务器,

面朝终端,春暖花开

从明天起,

和每一个 AI 通信 

告诉它我的代码 

那汹涌的Token闪电告诉我的 

我将告诉每一个人

给每一条慢 SQL 

取一个温暖的名字 

陌生人,我也为你祝福 

愿你有一个灿烂的前程 

你在 AI 浪潮中获得幸福 

愿你在技术更迭里找到归宿 

我只愿面朝终端,春暖花开

技术在变,工具在变,但好奇心和学习力,是唯一不会被 AI 取代的东西。愿你我,都能在这新时代的新质生产力--词元-Token的冲击下,能够找到属于自己那片海。


作者:Acdante

系列:YashanDB 学习篇

上一篇:崖山数据库备份恢复与闪回:关键时刻能救命的"后悔药"

下一篇预告:YashanDB 性能优化(二)——内存管理与参数调优实战


如果觉得有收获,欢迎点赞、在看、转发三连。你的支持是我继续写下去的动力。


文章转载自Acdante,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论