一、 插入数据优化
当需要插入大量数据时,普通的单条INSERT语句效率极低。
优化方案:
-
批量插入
sql-- 不推荐:多次网络I/O和事务开销 INSERT INTO table VALUES (1, 'a'); INSERT INTO table VALUES (2, 'b'); -- 推荐:一次插入多条数据 INSERT INTO table VALUES (1, 'a'), (2, 'b'), (3, 'c');
-
手动提交事务
sql-- 避免每条INSERT都自动提交事务 START TRANSACTION; INSERT INTO table VALUES (1, 'a'); INSERT INTO table VALUES (2, 'b'); COMMIT;
-
主键顺序插入
按主键的顺序插入数据,性能高于乱序插入。
-
使用LOAD指令
对于海量数据插入,可以使用MySQL的LOAD命令从文件加载。
sql-- 从本地文件加载数据 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 *,减少回表操作
-
示例:
sql
-- 索引: (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取消排序(如不需要排序结果)
-
遵循最左前缀法则
-
示例:
sql
-- 索引: (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分页的深分页问题:
sql
-- 效率低:OFFSET越大越慢
SELECT * FROM table LIMIT 1000000, 10;
优化方案:
-
覆盖索引 + 子查询
sqlSELECT * FROM table t JOIN (SELECT id FROM table ORDER BY id LIMIT 1000000, 10) tmp ON t.id = tmp.id;
-
基于上次位置的查询(游标分页)
sql-- 传统分页 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的行锁
-
基于索引加行锁
-
不基于索引加表锁
-
-
优化方案:
sql-- 索引: (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 ***:只查询需要的字段
-
大数据量操作:分批处理,避免大事务