MySQL SQL 调优
下载地址
https://www.zhengdianxue.com/wp-content/uploads/skills/org-e3rjs5bu--mysql-sql-tuning.zip将提示词发送给你的 AI 安装该 skills
请根据 https://skillhub.cn/install/skillhub.md,安装 @org-e3rjs5bu/mysql-sql-tuning。- 在编写、审查、解释或调优 MySQL / InnoDB SQL 时使用。当遇到以下情况时触发(Trigger):EXPLAIN type ALL、Using filesort、Using temporary、最左前缀、覆盖索引、OFFSET 分页、NOT IN、DISTINCT、LIKE '
- %keyword%'
- 、utf8mb4、utf8mb4_0900_ai_ci、中文索引、枚举存中文、慢查询,或者 InnoDB lock(锁)与 index(索引)性能问题。
阅读全文 (展开查看完整说明)
概述
MySQL(InnoDB)是单机/主从 OLTP 库。性能首先由有没有用上合适的索引、扫了多少行、有没有 filesort/临时表决定,不是由 Hive 倾斜或 GaussDB 分布列决定。
本技能基线:MySQL 8.0 + InnoDB。MPP 分布列用 gaussdb-sql-tuning,Hive / SparkSQL 用 hsql-tuning。
何时使用
- 写/改 MySQL 建表、索引、JOIN、分页、去重、模糊查询
- 慢查询、
EXPLAIN出现ALL/Using filesort/Using temporary - 中文乱码、emoji 存不下、状态字段存中文、索引长度超限
不要用本技能处理:Hive 分区分桶、GaussDB DISTRIBUTE BY、集群安装运维、给 root@% 开远程。
调优收益顺序
索引 > SQL 改写 > 锁与事务 > 参数 / 硬件
没有能用的索引,调 innodb_buffer_pool_size 也是在加速全表扫描。
写任何表之前
主键用窄的、单调递增的整数。字符集用完整 Unicode。状态不要存中文。
CREATE TABLE order_info (
order_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
cust_id BIGINT UNSIGNED NOT NULL,
order_status TINYINT UNSIGNED NOT NULL DEFAULT 1,
amount DECIMAL(12,2) NOT NULL,
created_at DATETIME NOT NULL,
PRIMARY KEY (order_id),
KEY idx_cust_created (cust_id, created_at)
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_0900_ai_ci;| 场景 | 选择 |
|---|---|
| 写多的 OLTP 表主键 | BIGINT UNSIGNED AUTO_INCREMENT |
| 外部 UUID | 唯一二级索引,不要当聚簇主键 |
| 状态 / 类型 / 性别 | TINYINT 或短英文码,禁止 '待发货' |
| 姓名等短中文内容 | VARCHAR + utf8mb4,等值/前缀可建索引 |
标题、详情的 '%词%' | 全文索引 / ngram,或同步到搜索引擎 |
旧库 utf8 / utf8mb3 | 改 utf8mb4,否则 emoji 和部分汉字写不进 |
索引设计:references/indexes.md 字符集与中文:references/charset.md
SQL 改写速查
| 不要 | 改成 |
|---|---|
SELECT * | 只要用到的列;能覆盖索引就覆盖 |
WHERE DATE(created_at)= / WHERE amount*1.2> | 把计算移到常量侧,让索引列保持原样 |
'%keyword%' | 能右模糊就 'keyword%';中文包含搜索走全文/ES |
OFFSET 100000 | 按上一页最大主键/排序键继续查 |
NOT IN (SELECT ...) | NOT EXISTS;NOT IN 遇 NULL 结果为空 |
SELECT DISTINCT ... | GROUP BY 业务键,并给这些键索引 |
一条 OR 打多个列 | 能走索引时拆成 UNION ALL |
| 大表 JOIN 后再过滤 | 单表先过滤再 JOIN |
LIMIT 无 ORDER BY 当稳定分页 | 显式排序键 + 键集分页 |
完整正反例:references/sql-patterns.md
诊断
先看 EXPLAIN,不要先改参数。
红旗:
type: ALL:全表扫描Extra: Using filesort:额外排序Extra: Using temporary:中间临时表key: NULL:没用上索引rows远大于最终返回行数:过滤太晚或索引选错
MySQL 8.0.18+ 用 EXPLAIN ANALYZE 看真实耗时。慢查询日志确认是哪条 SQL,而不是猜。
步骤:references/diagnose.md
交付检查
写完 SQL / 索引后按顺序回答,缺一项就改:
EXPLAIN是否还是ALL/key NULL/Using filesort?WHERE/ORDER BY/JOIN列是否在同一条最左前缀索引上?- 索引列有没有包函数?有没有
'%...%'? - 分页是否还在用大
OFFSET? - 是否还在用
SELECT *、DISTINCT、NOT IN? - 状态字段是否中文?表是否
utf8mb4?
常见借口
| 借口 | 事实 |
|---|---|
| 先把 SQL 跑通再补索引 | 没索引就是全表扫描;数据变大后补索引更痛。 |
| 多建几个索引总没错 | 二级索引含主键。写放大、Buffer Pool 被无用索引占满。 |
| 状态存中文更好读 | 代码硬编码、乱码即业务停摆、索引体积是整数的数倍。 |
utf8 已经能存中文 | MySQL 的 utf8 是 3 字节。emoji 和部分汉字需要 utf8mb4。 |
| 调大 buffer pool 就行 | 全表扫描加内存只是扫得更快,根因仍是索引或 SQL。 |
| 这和 Hive 一样先处理倾斜 | InnoDB 瓶颈是 B+Tree 定位和锁,不是 shuffle key。 |