文章目录
一、先看这个真实案例(血的教训)
上周排查线上问题时发现有个订单查询接口超时,查看慢日志发现一条看似简单的SQL竟然执行了8.7秒!!!
SELECT * FROM orders
WHERE user_id = 10086
AND create_time BETWEEN '2024-01-01' AND '2024-07-01'
ORDER BY amount DESC
LIMIT 10;
(别急着往下翻!先思考30秒:这个查询可能存在哪些问题?)
二、索引优化的四大金刚(附避坑指南)
2.1 联合索引的排列组合陷阱
刚开始给这个表建的索引是:
ALTER TABLE orders ADD INDEX idx_user (user_id);
执行计划显示:
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | key | rows | filtered| Extra| key_len| ref |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | orders | ref | idx_user | 3562 | 10.00 | Using where; Using filesort |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
发现问题了吗?filesort这个红色警报说明排序没走索引!!!
优化后的正确姿势:
ALTER TABLE orders ADD INDEX idx_user_time_amount (user_id, create_time, amount);
2.2 最左前缀原则的灵活运用
(敲黑板)联合索引就像电话号码的区号:
- 可以只用前几位(user_id)
- 不能跳过中间数字(user_id + amount)
- 可以模糊匹配最后几位(user_id + create_time + amount DESC)
2.3 隐式类型转换的坑
遇到过这种奇葩问题吗?
-- user_id是varchar类型时
SELECT * FROM users WHERE user_id = 10086; -- 全表扫描!
MySQL会默默把整型转成字符串,导致索引失效。解决方案就四个字:类型一致!
三、查询优化的三大绝招(实战技巧)
3.1 LIMIT分页的致命陷阱
经典分页查询:
SELECT * FROM orders
WHERE status=1
ORDER BY id
LIMIT 1000000, 10; -- 慢到怀疑人生!
优化方案:
SELECT * FROM orders
WHERE status=1 AND id > 1000000
ORDER BY id
LIMIT 10;
配合前端记住上次查询的最大ID,速度提升100倍不是梦!
3.2 避免SELECT * 的隐藏代价
某次排查发现一个统计接口:
SELECT * FROM user_logs
WHERE create_date = CURDATE(); -- 结果只要count(*)
实际扫描了所有字段,包括2个TEXT类型的日志内容。改成:
SELECT COUNT(1) FROM user_logs
WHERE create_date = CURDATE();
执行时间从3.2秒降到0.05秒!!!
3.3 巧用覆盖索引
看这个查询:
SELECT user_id, order_no FROM orders
WHERE status = 2;
如果创建(status, user_id, order_no)的联合索引,MySQL直接在索引里就能取到数据,不用回表查磁盘!
四、配置调优的五个关键参数(生产环境验证)
4.1 缓冲池大小
[mysqld]
innodb_buffer_pool_size = 16G # 建议设置为物理内存的60-70%
4.2 日志配置黄金组合
innodb_log_file_size = 2G
innodb_log_files_in_group = 3
sync_binlog = 1
innodb_flush_log_at_trx_commit = 2
4.3 连接数调优
max_connections = 2000
thread_cache_size = 100
wait_timeout = 600
五、架构优化的三种必杀技(千万级数据实战)
5.1 冷热数据分离
把半年前的订单数据迁移到历史表,查询性能提升3倍,同时节省**40%**存储空间
5.2 读写分离架构
通过ProxySQL实现:
- 写操作走主库
- 读操作走从库
- 自动故障转移
5.3 分区表实战
按月分区的大表查询:
-- 创建分区表
PARTITION BY RANGE (TO_DAYS(create_time)) (
PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')),
PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01')),
...
);
查询时自动命中对应分区,扫描数据量减少90%!
六、性能分析工具箱(DBA都在用)
6.1 慢查询日志分析
-- 开启慢查询日志
slow_query_log = 1
long_query_time = 1
推荐使用pt-query-digest工具分析慢日志
6.2 EXPLAIN执行计划详解
重点关注:
- type列(最好到ref/range)
- Extra列(避免Using temporary和Using filesort)
- rows列(扫描行数)
6.3 性能模式监控
SELECT * FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;
七、常见调优误区(血泪教训总结)
- 索引越多越好?错!每个索引都会降低写性能
- 把所有字段都建联合索引?错!注意最左前缀原则
- 盲目调整参数?错!一定要基准测试
- 过早优化?大忌!应该先满足功能再优化
- 忽视业务场景?致命!调优必须结合具体业务
八、终极调优心法
最后分享我的调优三板斧:
- 看执行计划(EXPLAIN是你的第一道防线)
- 抓慢查询(pt-query-digest分析)
- 做基准测试(sysbench或自定义压测)
记住:没有银弹!调优是一个持续的过程。上周刚优化好的SQL,可能因为数据量增长下周又出问题。保持监控,定期review,才是王道!
(看到这里的都是真爱!送你一个彩蛋:遇到死锁问题,试试SHOW ENGINE INNODB STATUS
查看最新死锁信息,比查日志快10倍!)