一、成因分析
- 未加索引:WHERE、JOIN、ORDER BY、GROUP BY等字段未建索引,导致全表扫描
- 索引未命中:SQL写法不当,索引失效
- 大表无分区/分表:数据量大,单表查询慢
- SQL语句低效:子查询、嵌套、函数、模糊匹配等
- 统计/聚合操作无优化:COUNT、SUM等全表聚合
- 数据类型不合理:字段类型过大、未规范化
二、排查与定位
1. 开启慢查询日志
MySQL配置:
SET GLOBAL slow_query_log = 1;
SET GLOBAL slow_query_log_file = '/var/log/mysql-slow.log';
SET GLOBAL long_query_time = 1; -- 超过1秒的SQL记录为慢查询
2. 分析慢查询日志
- 用
mysqldumpslow
、pt-query-digest
等工具分析慢SQL - 关注执行频率高、耗时长的SQL
3. EXPLAIN分析执行计划
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
- 关注
type
(最好为const
、ref
、range
),key
(用到索引),rows
(扫描行数)
三、优化思路与技巧
1. 为查询条件加索引
- WHERE、JOIN、ORDER BY、GROUP BY等常用字段加索引
- 唯一性高的字段优先加索引(如user_id、email、order_no)
示例:
ALTER TABLE users ADD INDEX idx_email (email);
ALTER TABLE orders ADD INDEX idx_userid_status (user_id, status);
2. 联合索引与索引顺序
- 多条件查询用联合索引,顺序要与SQL条件一致
- 最左前缀原则:WHERE user_id=1 AND status=1,索引(user_id, status)有效
3. 避免索引失效的写法
- 不要在索引字段上用函数、运算、隐式类型转换
- LIKE '%xxx’无法用索引,LIKE 'xxx%'可用
- OR连接的字段都要有索引
示例:
-- 索引失效
SELECT * FROM users WHERE LEFT(email, 3) = 'abc';
-- 索引有效
SELECT * FROM users WHERE email LIKE 'abc%';
4. 分页优化
- 大数据量分页用索引覆盖或延迟关联,避免OFFSET大时全表扫描
示例:
-- 慢:OFFSET越大越慢
SELECT * FROM orders ORDER BY id LIMIT 100000, 20;
-- 快:用索引覆盖
SELECT * FROM orders WHERE id > 100000 ORDER BY id LIMIT 20;
5. 分区/分表
- 大表按时间、用户ID等分区/分表,减少单表数据量
6. SQL语句优化
- 避免SELECT *,只查需要的字段
- 用JOIN代替子查询
- 聚合操作加索引或用中间表
7. 定期维护与监控
- 定期
ANALYZE TABLE
、OPTIMIZE TABLE
,保持索引和统计信息新鲜 - 监控慢SQL、索引命中率
四、实际操作流程举例
- 开启慢查询日志,收集慢SQL
- 用EXPLAIN分析慢SQL执行计划
- 为WHERE/JOIN/ORDER BY等字段加索引
- 优化SQL写法,避免索引失效
- 大表分区/分表,分页用索引覆盖
- 定期维护和监控,持续优化
五、PHP开发中的实践
1. 查询前用EXPLAIN分析SQL
$stmt = $pdo->query("EXPLAIN SELECT * FROM users WHERE email = 'test@example.com'");
print_r($stmt->fetchAll());
2. 用参数化查询防SQL注入
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
六、总结
- SQL慢查询、未加索引是数据库性能杀手
- 优化核心是加索引、写好SQL、分区分表、定期维护
- 先定位慢点,再针对性优化,持续监控
!