在数据库应用开发中,SQL语句的性能往往成为系统瓶颈。我曾处理过一个电商平台案例,某条未优化的商品查询SQL在促销期间导致整个数据库CPU飙升至100%,通过简单的索引优化后,查询时间从8秒降至50毫秒。这个经历让我深刻认识到SQL优化的重要性——它不仅影响单一查询效率,更关乎整个系统的稳定性。
当MySQL收到一条SELECT语句时,首先会进行语法解析(Parser),然后由优化器(Optimizer)生成执行计划。以这个查询为例:
sql复制SELECT * FROM orders WHERE user_id = 100 AND status = 'completed';
优化器会考虑:
我曾用EXPLAIN分析过一个实际案例:当表中存在(user_id)单列索引和(user_id,status)联合索引时,优化器选择了后者,因为其过滤性更好(Cardinality更高)。
通过EXPLAIN输出的几个关键字段:
经验:当看到type=ALL且rows超过1000时,这个查询就急需优化
最左前缀原则:对于联合索引(a,b,c),只有以下查询能利用索引:
索引选择性公式:
code复制选择性 = 不重复值数量(DISTINCT) / 总行数(COUNT)
通常选择性>10%的列适合建索引
覆盖索引优化:通过索引直接获取数据,避免回表。例如:
sql复制-- 需要回表
SELECT * FROM users WHERE age > 20;
-- 覆盖索引
SELECT id,age FROM users WHERE age > 20;
隐式类型转换:
sql复制-- user_id是varchar类型时
WHERE user_id = 100 -- 索引失效(转为数字比较)
WHERE user_id = '100' -- 使用索引
函数操作:
sql复制WHERE DATE(create_time) = '2023-01-01' -- 索引失效
WHERE create_time BETWEEN '2023-01-01 00:00:00' AND '2023-01-01 23:59:59' -- 使用索引
不合理的LIKE查询:
sql复制WHERE name LIKE '%张%' -- 全表扫描
WHERE name LIKE '张%' -- 可能使用索引
驱动表选择原则:
错误案例:
sql复制-- 大表user_log驱动小表users
SELECT * FROM user_log l JOIN users u ON l.user_id = u.id;
优化方案:
sql复制SELECT * FROM users u JOIN user_log l ON u.id = l.user_id;
子查询优化:
sql复制-- 低效的IN子查询
SELECT * FROM products
WHERE category_id IN (SELECT id FROM categories WHERE type=1);
-- 优化为JOIN
SELECT p.* FROM products p
JOIN categories c ON p.category_id = c.id AND c.type=1;
典型问题场景:
sql复制SELECT * FROM orders ORDER BY create_time DESC LIMIT 100000, 10;
优化方案:
延迟关联:
sql复制SELECT t.* FROM orders t
JOIN (SELECT id FROM orders ORDER BY create_time DESC LIMIT 100000, 10) tmp
ON t.id = tmp.id;
记录位点法(适用于有序字段):
sql复制-- 记住上一页最后一条记录的create_time
SELECT * FROM orders
WHERE create_time < '2023-06-01 12:00:00'
ORDER BY create_time DESC LIMIT 10;
短事务原则:
事务隔离级别选择:
常见死锁场景:
解决方案:
索引失效的常见场景:
EXPLAIN中的Using filesort:
题目:某订单查询接口响应慢,SQL如下:
sql复制SELECT * FROM orders
WHERE user_id=123 AND status IN (1,2,3)
ORDER BY create_time DESC
LIMIT 20;
优化思路:
配置参数:
code复制slow_query_log = ON
long_query_time = 1 # 超过1秒的查询
log_queries_not_using_indexes = ON
分析工具:
关键指标:
我在实际工作中发现,定期(如每周)分析慢查询日志,配合pt-index-usage工具检查未使用的索引,能持续提升数据库性能。