1. MySQL SQL调优核心思路解析
第一次在生产环境遇到慢查询时,我盯着那个执行时间长达8秒的SQL语句手足无措。DBA走过来只加了两个索引,查询立刻降到200毫秒——这个经历让我意识到SQL调优就像给数据库做"体检",需要系统化的诊断方法。以下是经过上百次实战验证的调优路径:
1.1 性能瓶颈定位三板斧
执行计划分析(EXPLAIN)是调优的起点。重点关注:
- type列:最好到ref级别,避免出现ALL全表扫描
- rows列:估算扫描行数,超过1万就需要警惕
- Extra列:出现"Using temporary"或"Using filesort"意味着需要优化
-- 经典分析案例 EXPLAIN SELECT * FROM orders WHERE user_id = 100 AND status = 'paid';慢查询日志需要关注这些参数:
slow_query_log = ON long_query_time = 1 # 超过1秒的查询 log_queries_not_using_indexes = ON性能监控工具推荐:
- Percona PMM:可视化监控利器
- MySQL Workbench:内置性能报告
- 自制脚本:定时采集SHOW STATUS值
1.2 索引优化黄金法则
索引不是越多越好,我有次给表加了15个索引反而导致写入性能下降70%。有效策略包括:
- 最左前缀原则实战:
-- 联合索引 (a,b,c) 生效场景: WHERE a = 1 AND b = 2 WHERE a = 1 ORDER BY b WHERE a = 1 AND b > 2 AND c = 3 -- 不生效场景: WHERE b = 2 -- 缺少最左列 WHERE a = 1 ORDER BY c -- 跳过了b列- 索引选择性计算公式:
# 选择性 = 不重复值数量 / 总记录数 selectivity = count(distinct column) / count(*) # 高于0.1的列适合建索引- 隐式类型转换陷阱:
-- user_id是varchar类型时 SELECT * FROM users WHERE user_id = 100; -- 全表扫描 SELECT * FROM users WHERE user_id = '100'; -- 走索引2. 高级调优技巧实战
2.1 查询重写艺术
分页优化是高频需求。常见错误写法:
SELECT * FROM products LIMIT 1000000, 20; -- 扫描100万+20行优化方案(延迟关联):
SELECT * FROM products INNER JOIN ( SELECT id FROM products ORDER BY create_time DESC LIMIT 1000000, 20 ) AS tmp USING(id);COUNT优化的三种方案对比:
- 精确计数(适合小表):
SELECT COUNT(*) FROM users;- 估算计数(InnoDB引擎):
SHOW TABLE STATUS LIKE 'users'; -- 查看Rows字段- 维护计数表(高频访问场景):
-- 创建计数器表 UPDATE counter SET value = value + 1 WHERE name = 'users';2.2 事务与锁优化
死锁分析流程:
- 开启监控:
SET GLOBAL innodb_print_all_deadlocks = ON;- 查看最近死锁:
SHOW ENGINE INNODB STATUS\G- 典型死锁场景:
-- 事务1 UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 事务2(相反顺序) UPDATE accounts SET balance = balance + 200 WHERE id = 2; UPDATE accounts SET balance = balance - 200 WHERE id = 1;锁超时优化参数:
innodb_lock_wait_timeout = 50 # 默认50秒 innodb_rollback_on_timeout = ON3. 配置参数调优
3.1 内存参数精调
缓冲池配置公式:
# 建议值为可用内存的50%-75% innodb_buffer_pool_size = total_ram * 0.7 # 多实例场景需要分片 innodb_buffer_pool_instances = 8 # 每个实例至少1GB连接数优化方法:
-- 查看当前连接状态 SHOW STATUS LIKE 'Threads_%'; -- 计算最大连接数需求 max_connections = (max_user_connections * 用户数) * 1.23.2 磁盘I/O优化
日志文件配置原则:
innodb_log_file_size = 1G # 建议1-2小时写入量 innodb_log_files_in_group = 3 # 循环写入 sync_binlog = 1 # 事务安全但影响性能临时表优化:
-- 监控临时表使用 SHOW STATUS LIKE 'Created_tmp%'; -- 内存临时表大小 tmp_table_size = 64M max_heap_table_size = 64M4. 实战避坑指南
4.1 索引失效的7种场景
- 使用NOT、!=、<>操作符
- 对索引列使用函数
- 类型不匹配(如字符串当数字用)
- OR条件未全覆盖
- LIKE以通配符开头
- 隐式字符集转换
- 索引列参与计算
4.2 慢查询紧急处理五步法
- 使用SHOW PROCESSLIST定位问题会话
- 对长时间运行的查询执行KILL命令
- 临时增加相关索引
- 重写复杂查询为多个简单查询
- 考虑使用查询缓存(MySQL 8.0前)
4.3 监控指标警戒值
| 指标名称 | 警戒值 | 检查方法 |
|---|---|---|
| CPU利用率 | >70% | SHOW STATUS LIKE 'cpu%' |
| 内存交换 | >0 | 操作系统free -m命令 |
| 磁盘队列长度 | >2 | iostat -x 1 |
| 锁等待率 | >5% | SHOW STATUS LIKE 'innodb_row_lock%' |
| 临时表磁盘使用 | >10% | SHOW STATUS LIKE 'Created_tmp_disk_tables' |
曾经有个电商系统在促销时崩溃,后来发现是因为商品表的VARCHAR(255)字段被错误地建了全文索引。这个教训让我明白:调优工具再强大,也比不上对业务数据的深刻理解。每次调优前,先花10分钟搞清楚这些数据是怎么产生和使用的,往往能事半功倍。