MySQL 的 10 种高级 SQL 技巧,性能飞升(2026 年最新实战版)
以下 10 条是 2025–2026 年 MySQL(8.0/8.4/9.x 系列)生产环境中最常用来提速 5–50 倍的高级写法和优化思路,按实用频度从高到低排序。每条都带真实场景、优化前后对比和关键注意点。
1. 覆盖索引 + 强制索引顺序(covering index + index hint)
-- 优化前(回表 2 次)
SELECT id, name, create_time FROM users WHERE status=1 ORDER BY create_time DESC LIMIT 10;
-- 优化后(单次索引覆盖,无回表)
CREATE INDEX idx_status_create ON users(status, create_time, id, name);
SELECT id, name, create_time
FROM users FORCE INDEX(idx_status_create)
WHERE status=1
ORDER BY create_time DESC LIMIT 10;
提速倍数:3–20 倍(大表常见)
适用:分页列表、排行榜、feed 流
2026 新提示:MySQL 8.4+ 对覆盖索引排序的代价估算更准,FORCE INDEX 胜率更高。
2. 延迟物化 + 子查询转 JOIN(late materialization)
-- 优化前(全表扫 + 子查询)
SELECT * FROM orders
WHERE user_id IN (SELECT user_id FROM users WHERE vip_level >= 3);
-- 优化后(先取少量 id 再 join)
SELECT o.*
FROM orders o
INNER JOIN (
SELECT user_id FROM users WHERE vip_level >= 3 LIMIT 5000
) t ON o.user_id = t.user_id;
提速倍数:5–30 倍(子查询结果集小时特别明显)
适用:IN 子查询结果集 < 1% 总行数时
3. 索引条件下推(Index Condition Pushdown)+ 函数索引
-- 8.0 前必须建 (LEFT(phone,7))
CREATE INDEX idx_phone_prefix ON users ((LEFT(phone,7)));
-- 现在直接用函数索引(MySQL 8.0+)
SELECT * FROM users
WHERE LEFT(phone,7) = '1381234';
额外加分:JSON_EXTRACT、JSON_VALUE 也能建函数索引(8.0.21+)
4. 批量 + ON DUPLICATE KEY UPDATE(upsert 批量插入)
INSERT INTO user_stats (user_id, views, likes, updated_at)
VALUES
(1001, 5, 2, NOW()),
(1002, 3, 1, NOW()),
(1003, 7, 0, NOW())
ON DUPLICATE KEY UPDATE
views = views + VALUES(views),
likes = likes + VALUES(likes),
updated_at = VALUES(updated_at);
提速倍数:10–100 倍(对比单条 update/insert)
适用:实时埋点统计、UV/PV 计数、排行榜增量更新
5. 巧用窗口函数替代自连接 / 子查询(Window Function)
-- 优化前(自连接)
SELECT t1.id, t1.score, COUNT(*) rank
FROM scores t1
JOIN scores t2 ON t2.score >= t1.score
GROUP BY t1.id;
-- 优化后(窗口函数,MySQL 8.0+)
SELECT id, score,
DENSE_RANK() OVER (ORDER BY score DESC) AS `rank`
FROM scores;
提速倍数:5–40 倍(分组排名、去重、累计求和场景)
6. 避免大分页(深分页优化)—— 使用覆盖 + 延迟
-- 坏写法(扫描 100000 行再丢弃前 99990 行)
SELECT * FROM logs ORDER BY id DESC LIMIT 99990, 10;
-- 好写法(记住上一页最后 id)
SELECT * FROM logs
WHERE id < 最后一条id
ORDER BY id DESC LIMIT 10;
极致写法(结合覆盖索引):
SELECT id FROM logs FORCE INDEX(idx_create_time)
WHERE create_time < '最后时间'
ORDER BY create_time DESC LIMIT 10;
提速倍数:几十到几百倍(page=10000+ 时)
7. 强制索引 + 索引合并(Index Merge)控制
-- 让优化器选择 union 索引而非全扫
SELECT * FROM orders FORCE INDEX(idx_status, idx_create)
WHERE (status=1 OR status=2) AND create_time > '2025-01-01';
2026 小技巧:USE INDEX FOR ORDER BY / FOR GROUP BY 单独控制排序/分组索引
8. 聚合查询下推 + HAVING 提前过滤
-- 优化前
SELECT user_id, COUNT(*) cnt
FROM events
GROUP BY user_id
HAVING cnt > 100;
-- 优化后(where 先过滤)
SELECT user_id, COUNT(*) cnt
FROM events
WHERE event_type IN ('login','pay') -- 假设只统计这两种
GROUP BY user_id
HAVING cnt > 100;
提速倍数:2–15 倍(聚合前数据量减少明显)
9. 利用直方图 + 持久化统计信息(MySQL 8.0+)
-- 提升不等值/范围查询准确度
ANALYZE TABLE users UPDATE HISTOGRAM ON age WITH 256 BUCKETS;
ANALYZE TABLE orders PERSISTENT FOR COLUMNS (status, create_time);
效果:IN 子句、BETWEEN、> < 条件下的基数估计更准,执行计划崩的概率大幅降低
10. CTE + 可物化 CTE(MySQL 8.0+ / 8.4 增强)
WITH RECURSIVE date_range AS (
SELECT DATE('2025-01-01') AS dt
UNION ALL
SELECT dt + INTERVAL 1 DAY FROM date_range WHERE dt < '2025-12-31'
),
daily_stats AS (
SELECT /*+ MATERIALIZED */ -- 8.4+ 强制物化,避免重复计算
dt, COUNT(*) orders
FROM date_range
LEFT JOIN orders o ON DATE(o.create_time) = dt
GROUP BY dt
)
SELECT * FROM daily_stats ORDER BY dt;
提速倍数:3–20 倍(复杂报表、连续日期补 0、树形递归)
快速对照表(2026 年最值钱 10 条)
| 排名 | 技巧 | 典型提速倍数 | 难度 | 出现频率 |
|---|---|---|---|---|
| 1 | 覆盖索引 + FORCE INDEX | 3–20× | ★★☆ | ★★★★★ |
| 2 | 延迟物化子查询 | 5–30× | ★★★ | ★★★★☆ |
| 3 | 函数索引 / JSON索引 | 5–50× | ★★☆ | ★★★★☆ |
| 4 | INSERT … ON DUPLICATE | 10–100× | ★☆☆ | ★★★★★ |
| 5 | 窗口函数替换自连接 | 5–40× | ★★★ | ★★★★☆ |
| 6 | 深分页 → 游标式 | 10–500× | ★★☆ | ★★★★★ |
| 7 | Index Merge 控制 | 2–15× | ★★★ | ★★★☆☆ |
| 8 | 聚合前 where 下推 | 2–15× | ★★☆ | ★★★★☆ |
| 9 | 直方图 + 持久化统计 | 2–10×(防崩) | ★★☆ | ★★★★☆ |
| 10 | 可物化 CTE | 3–20× | ★★★★ | ★★★☆☆ |
你目前项目里最痛的慢 SQL 是哪种类型?
(分页深?JOIN 多?GROUP BY 慢?范围扫描?实时统计?子查询爆炸?)
告诉我表结构 + 慢查询原文,我可以直接帮你改出高性能版本。