摘要
数据库执行计划绑定(SQL Plan Management,SPM)是保障查询性能稳定性的关键技术,能够防止因统计信息变化或优化器版本升级导致的执行计划跳变。本文系统介绍 SPM 的核心概念、TiDB 的实现机制,以及与 SQL Patch、Optimizer Hints 的对比选型。
本文适合谁:数据库管理员、DBA、后端开发工程师,以及正在排查 SQL 性能不稳定的 TiDB 用户。
一、执行计划为什么会"跳变"
在生产环境中,SQL 执行计划并非一成不变。以下场景是导致计划跳变的常见原因:
| 触发因素 | 典型表现 | 影响范围 |
|---|---|---|
| 统计信息自动更新 | Analyze 收集后索引选择变化 | 单条或一批 SQL |
| 数据量大幅增长 | 表从千行到千万行,Join 策略改变 | 涉及大表的查询 |
| 优化器版本升级 | 新版本引入更优(或更差)的计划选择逻辑 | 全局 |
| 参数配置变更 | `tidb_opt_range_max_size` 等参数调整 | 特定模式查询 |
典型故障场景:某电商订单查询语句在凌晨统计信息自动收集后,从使用二级索引的 IndexScan 切换为全表扫描(FullTableScan),导致响应时间从 5ms 飙升到 3s,直接影响交易链路。
二、SQL Plan Management(SPM)核心概念
SPM 是 Oracle 在 11g 中引入并被行业广泛采用的概念,核心思想是:
- 建立基线(Plan Baseline):记录经过验证的高效执行计划
- 管控演进:新计划只有通过性能验证后才被纳入基线
- 防御退化:拒绝未经验证的计划变更,保障线上稳定
SPM 的三个关键状态:
┌──────────────┐ 验证通过 ┌──────────────┐
│ Accepted │ ──────────→ │ Accepted │(新计划加入基线)
│ (已接受) │ │ (已接受) │
└──────────────┘ └──────────────┘
↑ │
│ 验证未通过 │ 优化器生成新计划
│ ↓
┌──────────────┐ ┌──────────────┐
│ Rejected │ ←────────── │ Unverified │
│ (已拒绝) │ 性能更差 │ (未验证) │
└──────────────┘ └──────────────┘
三、TiDB SPM 实现机制
3.1 Plan Baseline(计划基线)
TiDB 通过 `mysql.plan_baseline` 系统表存储计划基线:
SELECT * FROM mysql.plan_baseline\G
每条基线记录包含:
- `normalized_sql`:SQL 的标准化形式(去除常量后的指纹)
- `plan_id`:执行计划的唯一标识
- `plan_digest`:计划的摘要信息
- `status`:计划状态(`accepted` / `pending` / `rejected`)
3.2 手动绑定(Manual Binding)
通过 SQL 语句手动将特定执行计划绑定到 SQL 指纹:
-- 第一步:从 Statement Summary 中找到需要绑定的 SQL
SELECT digest_text, plan_digest, plan
FROM information_schema.statements_summary
WHERE digest_text LIKE '%order_items%'
ORDER BY sum_latency DESC LIMIT 5;
-- 第二步:创建绑定
CREATE BINDING FOR
SELECT * FROM order_items WHERE order_id = ?
USING
SELECT * FROM order_items USE INDEX (idx_order_id) WHERE order_id = ?;
也可以通过图形化界面在 TiDB Dashboard 的 SQL Tuning 页面一键绑定。
3.3 自动演进(Automatic Evolution)
TiDB SPM 支持自动演进机制:
-- 启用自动演进
SET GLOBAL tidb_enable_auto_evolution = ON;
-- 查看自动演进状态
SHOW VARIABLES LIKE 'tidb_enable_auto_evolution';
自动演进的工作流程:
- 优化器生成新计划,但当前有 `accepted` 状态的基线存在
- 新计划进入 `unverified` 状态,在后台执行性能对比测试
- 如果新计划执行时间显著优于基线(默认阈值),自动升级为 `accepted`
- 如果性能更差,标记为 `rejected`
3.4 管理绑定
-- 查看当前所有绑定
SHOW BINDINGS;
-- 删除绑定
DROP BINDING FOR SELECT * FROM order_items WHERE order_id = ?;
-- 导出绑定(用于跨环境迁移)
SELECT * FROM mysql.plan_baseline INTO OUTFILE '/tmp/bindings.csv';
四、SPM vs SQL Patch vs Optimizer Hints
| 维度 | SPM(计划绑定) | SQL Patch | Optimizer Hints |
|---|---|---|---|
| 生效粒度 | SQL 指纹级别 | SQL 指纹级别 | 单条 SQL 语句 |
| 侵入性 | 低,不改 SQL | 低,不改 SQL | 高,需修改应用 SQL |
| 稳定性保障 | 高,有验证机制 | 中,直接替换计划 | 低,无验证 |
| 自动演进 | 支持 | 不支持 | 不支持 |
| 跨环境迁移 | 导出/导入 | 导出/导入 | 随应用代码 |
| 推荐场景 | 生产环境长期稳定化 | 紧急修复计划退化 | 开发调试、临时调优 |
选型建议:
- 生产环境首选 SPM:稳定、可控、支持演进
- 紧急修复用 SQL Patch:当需要快速替换计划时
- 开发调试用 Hints:灵活但不应长期依赖
五、SPM 最佳实践
5.1 建立绑定流程
发现慢 SQL → 分析执行计划 → 确认优化方案 → 创建绑定 → 验证效果 → 归档
5.2 关键配置建议
-- 建议开启 SPM 相关配置
SET GLOBAL tidb_enable_plan_binding = ON;
SET GLOBAL tidb_enable_auto_evolution = ON;
-- 建议设置合理的演进阈值(默认较保守)
SET GLOBAL tidb_auto_evolution_time_threshold = '1s';
5.3 注意事项
- 定期审查绑定:长期未使用的绑定应清理,避免影响优化器演进
- DDL 变更后检查:表结构变更可能导致绑定失效
- 版本升级后验证:新版本优化器改进后,建议重新评估绑定必要性
- 配合统计信息管理:SPM 不能替代合理的统计信息收集策略
FAQ
Q1:SPM 会影响所有 SQL 的性能吗? 不会。SPM 仅对已创建绑定的 SQL 指纹生效,未绑定的 SQL 仍然由优化器自由选择执行计划。
Q2:自动演进会引入性能退化风险吗? 风险极低。TiDB 的自动演进要求新计划在实测中显著优于基线才会被接受,且整个过程在后台执行,不影响线上流量。
Q3:如何在升级 TiDB 后检查现有绑定是否仍然有效? 升级后执行 `SHOW BINDINGS` 查看绑定状态,对 `rejected` 状态的绑定重新评估,并关注 `tidb_enable_auto_evolution` 是否捕捉到更优的新计划。
Q4:SPM 绑定能否跨集群迁移? 可以。通过导出 `mysql.plan_baseline` 数据,在新集群导入即可实现绑定迁移,适合从测试环境到生产环境的迁移场景。
总结
执行计划绑定(SPM)是数据库性能稳定性的关键防线。TiDB 的 SPM 实现提供了手动绑定、自动演进、基线管理三大能力,能够在不修改应用 SQL 的前提下,有效防止执行计划跳变导致的性能故障。对于生产环境中的关键业务 SQL,推荐将 SPM 作为常态化性能管理手段。
下一步行动
- 试用 TiDB SPM:TiDB 免费试用 — 在云上体验 SPM 功能
- 获取调优方案:联系 PingCAP 技术专家 — 获取 SQL 性能诊断与调优服务
- 深入阅读:TiDB SPM 官方文档 — 了解完整配置与使用方法