MySQL进阶知识点(八)---- SQL优化
一、 插入数据优化
当需要插入大量数据时,普通的单条INSERT语句效率极低。
优化方案:
批量插入
-- 不推荐:多次网络I/O和事务开销 INSERT INTO table VALUES (1, 'a'); INSERT INTO table VALUES (2, 'b');-- 推荐:一次插入多条数据 INSERT INTO table VALUES (1, 'a'), (2, 'b'), (3, 'c');
手动提交事务
-- 避免每条INSERT都自动提交事务 START TRANSACTION; INSERT INTO table VALUES (1, 'a'); INSERT INTO table VALUES (2, 'b'); COMMIT;
主键顺序插入
按主键的顺序插入数据,性能高于乱序插入。使用LOAD指令
对于海量数据插入,可以使用MySQL的LOAD命令从文件加载。-- 从本地文件加载数据 LOAD DATA LOCAL INFILE '/path/to/file.csv' INTO TABLE table_name FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';
二、 主键优化
InnoDB的数据组织方式
InnoDB使用B+Tree索引组织数据
所有数据都存储在聚簇索引的叶子节点
页分裂与页合并
页分裂:当页已满,新插入的数据会导致页分裂,影响性能
页合并:当页中数据删除达到阈值(MERGE_THRESHOLD,默认50%),InnoDB会寻找相邻页合并
主键设计原则
尽量短:二级索引叶子节点存储主键,主键过长会占用更多空间
顺序插入:使用AUTO_INCREMENT或雪花算法等,避免随机主键
避免频繁更新:主键更新会导致二级索引同步更新
三、 ORDER BY优化
Using filesort的情况
通过磁盘文件或内存进行排序,效率较低
在EXPLAIN的Extra字段中显示
Using filesort
Using index的情况
通过索引直接完成排序,效率高
在EXPLAIN的Extra字段中显示
Using index
优化方案:
为ORDER BY的字段建立合适的索引
遵循最左前缀法则
多字段排序时,保持ORDER BY顺序与索引顺序一致
避免SELECT *,减少回表操作
示例:
-- 索引: (age, salary)
-- 有效:使用索引排序
EXPLAIN SELECT id FROM employees ORDER BY age, salary;-- 无效:出现Using filesort
EXPLAIN SELECT id FROM employees ORDER BY salary, age;
四、 GROUP BY优化
Using temporary的情况
使用临时表进行分组,效率较低
在EXPLAIN的Extra字段中显示
Using temporary
优化方案:
为GROUP BY字段建立索引
在GROUP BY后使用ORDER BY NULL取消排序(如不需要排序结果)
遵循最左前缀法则
示例:
-- 索引: (dept_id)
-- 优化前:可能使用临时表
EXPLAIN SELECT dept_id, COUNT(*) FROM employees GROUP BY dept_id;-- 优化后:使用索引,取消排序
EXPLAIN SELECT dept_id, COUNT(*) FROM employees GROUP BY dept_id ORDER BY NULL;
五、 LIMIT优化
在大数据量下,LIMIT分页的深分页问题:
-- 效率低:OFFSET越大越慢
SELECT * FROM table LIMIT 1000000, 10;
优化方案:
覆盖索引 + 子查询
SELECT * FROM table t JOIN (SELECT id FROM table ORDER BY id LIMIT 1000000, 10) tmp ON t.id = tmp.id;
基于上次位置的查询(游标分页)
-- 传统分页 SELECT * FROM table ORDER BY id LIMIT 20;-- 下一页(记录上次的最大id) SELECT * FROM table WHERE id > 上次的最大id ORDER BY id LIMIT 20;
六、 COUNT优化
COUNT的几种用法
COUNT(主键)
:InnoDB遍历整表,把主键值取出,按行累加COUNT(字段)
:统计该字段不为NULL的数量COUNT(1)
:InnoDB遍历整表,但不取值,按行累加COUNT(*)
:MySQL做了优化,不取值,按行累加
效率对比
COUNT(*)
≈COUNT(1)
>COUNT(主键)
>COUNT(字段)
优化方案:
使用
COUNT(*)
,MySQL会自行优化维护计数表,实时更新总数
使用Redis等外部缓存记录总数
七、 UPDATE优化
核心原则: 避免表锁,使用行锁。
InnoDB的行锁
基于索引加行锁
不基于索引加表锁
优化方案:
-- 索引: (id) -- 高效:使用行锁 UPDATE table SET name = 'new' WHERE id = 1;-- 危险:无索引,使用表锁,阻塞其他操作 UPDATE table SET name = 'new' WHERE name = 'old';
最佳实践
确保WHERE条件使用索引
避免在事务中执行大量UPDATE
分批更新大数据量
优化总结表格
优化场景 | 核心问题 | 优化方案 |
---|---|---|
插入数据 | 单条插入效率低 | 批量插入、手动事务、LOAD DATA |
主键设计 | 页分裂、空间占用 | 短主键、顺序插入、避免更新 |
ORDER BY | Using filesort | 创建合适索引、遵循最左前缀 |
GROUP BY | Using temporary | 创建索引、ORDER BY NULL取消排序 |
LIMIT | 深分页性能差 | 覆盖索引+子查询、游标分页 |
COUNT | 全表扫描效率低 | 使用COUNT(*)、维护计数表 |
UPDATE | 表锁阻塞 | WHERE条件必须使用索引 |
通用优化建议
善用EXPLAIN:分析SQL执行计划是优化的第一步
索引是双刃剑:合理创建,避免过多
**避免SELECT ***:只查询需要的字段
大数据量操作:分批处理,避免大事务