MySQL 高效 SQL 使用 技巧详解
一、为什么需要优化 SQL?
- 性能瓶颈:慢查询导致数据库负载升高,响应时间延长。
- 资源浪费:低效 SQL 可能占用大量 CPU、内存和磁盘 I/O。
目标:通过优化 SQL 将查询性能提升 10 倍以上,降低硬件成本。
二、核心优化原则
- 索引优先:为高频查询字段添加索引。
- 减少数据量:避免全表扫描,缩小查询范围。
- 简化逻辑:用 JOIN 替代子查询,避免复杂嵌套。
- 事务控制:缩短事务长度,减少锁竞争。
三、高效 SQL 实战技巧
1. 索引优化
(1)创建复合索引
-- 为 (user_id, create_time) 创建复合索引
CREATE INDEX idx_user_time ON orders(user_id, create_time);
适用场景:排序、范围查询(如 BETWEEN
)。
(2)覆盖索引
-- 查询字段包含在索引中,避免回表
SELECT user_id, create_time FROM orders WHERE user_id = 100;
索引建议:
CREATE INDEX idx_user_time_covering ON orders(user_id, create_time) INCLUDE (status);
(3)避免索引失效
错误写法 | 正确写法 | 原因 |
---|---|---|
WHERE price > 100 OR status = 'paid' | WHERE price > 100 AND status = 'paid' | OR 导致索引失效 |
WHERE name LIKE '%test' | WHERE name LIKE 'test%' | 前缀模糊查询可利用索引 |
2. 查询优化
(1)使用 EXPLAIN
分析执行计划
EXPLAIN SELECT * FROM users WHERE age > 30;
关键指标:
type
: 连接类型(理想值:ref
、eq_ref
)。key
: 使用的索引(NULL
表示全表扫描)。rows
: 预估扫描行数(越小越好)。
(2)分页优化
原查询(慢):
SELECT * FROM products LIMIT 100000, 10;
优化后(利用覆盖索引):
SELECT id, name FROM products
WHERE id > (SELECT id FROM products LIMIT 100000, 1)
LIMIT 10;
(3)批量操作替代循环
错误写法:
for item in data:cursor.execute("INSERT INTO logs VALUES (%s)", item)
正确写法:
INSERT INTO logs (col1, col2) VALUES
(1, 'a'), (2, 'b'), (3, 'c');
3. 事务与锁优化
(1)减少锁粒度
-- 行级锁示例(InnoDB 默认)
UPDATE users SET balance = balance - 100 WHERE id = 100;
(2)缩短事务长度
反模式:
START TRANSACTION;
SELECT * FROM large_table; -- 耗时操作
UPDATE orders SET status = 'paid';
COMMIT;
优化后:
START TRANSACTION;
UPDATE orders SET status = 'paid';
COMMIT;
4. 存储引擎选择
引擎 | 特点 | 适用场景 |
---|---|---|
InnoDB | 支持事务、行级锁、外键 | 高并发读写(如电商订单) |
MyISAM | 表级锁、不支持事务 | 读多写少(如日志表) |
Memory | 内存存储、速度极快 | 临时数据缓存 |
四、高级优化技巧
1. 子查询转 JOIN
原查询:
SELECT * FROM orders
WHERE user_id IN (SELECT id FROM users WHERE city = 'Shanghai');
优化后:
SELECT o.*
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE u.city = 'Shanghai';
2. 避免 SELECT *
反模式:
SELECT * FROM employees;
正确写法:
SELECT id, name, email FROM employees;
3. 使用 LIMIT
限制结果集
-- 快速获取最新记录
SELECT * FROM logs ORDER BY create_time DESC LIMIT 10;
五、实战案例:优化电商订单查询
优化前 SQL
SELECT * FROM orders
WHERE user_id = 100 AND status = 'paid'
ORDER BY create_time DESC;
问题:
- 全表扫描(无索引)。
- 回表查询所有字段。
优化步骤
-
添加复合索引:
CREATE INDEX idx_user_status_time ON orders(user_id, status, create_time);
-
仅查询必要字段:
SELECT id, amount, create_time FROM orders WHERE user_id = 100 AND status = 'paid' ORDER BY create_time DESC;
优化后效果
- 查询时间:从 2.1 秒降至 0.05 秒。
- 索引覆盖度:100%(无需回表)。
六、注意事项
1. 索引维护成本
- 索引过多会降低写入性能。
- 建议:为查询频率高的字段创建索引。
2. 慢查询日志
-- 开启慢查询日志(超过 2 秒的查询)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2;
3. 测试环境验证
# 使用 sysbench 压测
sysbench --test=oltp --oltp-table-size=1000000 --mysql-user=root run
七、总结:高效 SQL 检查表
优化项 | 操作 | 工具/命令 |
---|---|---|
索引 | 使用 EXPLAIN 分析执行计划 | EXPLAIN |
查询 | 避免 SELECT * ,使用覆盖索引 | EXPLAIN |
事务 | 缩短事务长度,减少锁竞争 | 监控 InnoDB_row_lock_waits |
存储引擎 | 根据场景选择合适引擎 | SHOW TABLE STATUS |
慢查询 | 开启慢查询日志并分析 | pt-query-digest |