目录
1. 插入数据优化
2.主键优化
3.order by 排序优化
4.group by 分组优化
5.limit优化
count 的几种用法
count(主键)
count(字段)
count (1)
count (*)
7.update语句优化
1. 插入数据优化
1.1 批量插入数据
如果数据量很多,循环单条插入,频繁事务、频繁IO,而批量一次插入,仅1次磁盘IO
1.2 手动控制事务,批量提交
1.3主键有序自增,避免页分裂自增 INT 主键,新增数据永远追加到 B + 树末尾,不会中间拆分数据页; 若用 UUID 无序主键,插入会频繁挪动页数据,写入暴跌。
1.4大批量导入用 LOAD DATA INFILE百万级数据导入,比 INSERT 快几十倍,适合数据同步场景。
优势:
- 交互开销极低多条 INSERT 需要客户端和 MySQL 反复网络交互、多次事务刷盘;LOAD DATA 是一次性读取本地文件,单次 IO 批量写入,减少大量网络往返。
- 事务与日志优化INSERT 每条 / 每批都会频繁刷新 redo/undo 日志;LOAD DATA 底层做批量页写入,日志合并刷盘,磁盘 IO 次数大幅降低。
- 索引延迟构建导入时可以临时关闭唯一索引校验,导入完成后统一构建索引,避免插入一行维护一次 B + 树。
- 解析开销小直接解析文本文件(csv/txt),不需要解析多条 INSERT SQL 语法,MySQL 解析器压力极小。
2.主键优化
无序主键插入可能导频繁页分裂
- 禁止 UUID、字符串做主键UUID 无序,插入频繁页分裂;字符串主键占用空间大,二级索引膨胀。
- 每张表必须显式主键无主键时 InnoDB 自动生成隐藏 6 字节 rowid,无索引优化,联表、查询变慢。
- 主键不要频繁修改主键是聚簇索引排序依据,改主键会挪动整行数据 + 所有二级索引同步更新。
- 优先自增 INT/BIGINT 做主键有序自增:插入不会页分裂;
3.order by 排序优化
mysql中的两种排序方式
①. Using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区 sort buffer 中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。
②. Using index:通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要额外排序,操作效率高。
如果索引没有覆盖order by的字段,就会走filesort,性能很低
业务需求:查询 1 班学生,按总分降序
sql
-- 原始慢SQL SELECT * FROM student WHERE class_id=1 ORDER BY total_score DESC;- 联合索引覆盖 where+order by,消除 filesort建索引:
idx_class_score(class_id, total_score)索引内数据天然按 class_id 分组、同班级按 score 有序,筛选后无需额外排序。
sql
CREATE INDEX idx_class_score ON student(class_id,total_score);- 排序方向统一索引升降序要和 SQL 一致,否则无法利用有序性:
where class_id=1 order by total_score desc索引也要按 score 降序定义。 - *不要 select,用覆盖索引
sql
-- 覆盖索引,无回表+无排序 SELECT class_id,stu_name,total_score FROM student WHERE class_id=1 ORDER BY total_score;- 避免排序字段使用函数、运算
ORDER BY YEAR(create_time)索引失效,强制文件排序。 - 限制排序结果集大数据量先缩小 where 过滤范围,再排序,不要全表排序。
一条联合索引里,包含当前这条 SQL 查询需要用到的所有字段,MySQL 只需要扫描索引树就能拿到全部数据,不需要回表(访问聚簇索引完整行数据),这种索引就叫覆盖索引。
4.group by 分组优化
无索引分组出现Using temporary,创建临时表分组聚合,消耗内存 / 磁盘。
- 分组字段建立前置联合索引索引:
idx_class_score(class_id,total_score)索引中相同 class_id 数据连续存放,MySQL 扫描索引时直接边扫边聚合,不用临时表。
group by 使用完整注意事项
MySQL5.7/8.0 默认开启
only_full_group_by:SELECT 后所有非聚合字段,必须全部写在 GROUP BY 后面
5.limit优化
一个常见又非常头疼的问题就是 limit 2000000,10 ,此时需要 MySQL 排序前 2000010 记录,仅仅返回 2000000-2000010 的记录,其他记录丢弃,查询排序的代价非常大。
优化思路:一般分页查询时,通过创建覆盖索引能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化。
子查询先定位偏移主键,再关联查详情
6.count优化
- MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count (*) 的时候会直接返回这个数,效率很高;
- InnoDB 引擎就麻烦了,它执行 count (*) 的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。
count 的几种用法
count(主键)
InnoDB 引擎会遍历整张表,把每一行的主键 id 值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加 (主键不可能为 null)。
count(字段)
没有 not null 约束:InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,服务判断是否为 null,不为 null,计数累加。
有 not null 约束:InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,直接按行进行累加。
count (1)
InnoDB 引擎遍历整张表,但不取值。服务层对于返回的每一行,放一个数字 “1” 进去,直接按行进行累加。
count (*)
InnoDB 引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加。
按照效率排序的话,count (字段) < count (主键 id) < count (1) ≈ count (*),所以尽量使用 count (*)。
- count (字段):读字段 + 判 NULL → 最多 IO+CPU,最慢
- count (主键):读主键 id → 有读取开销,中等速度
- count(1) / count(*):不读取任何字段,只统计行数 → 最少 IO、无数据拷贝,速度最快
7.update语句优化
更新字段时,一定要根据索引字段更新值,此时是行锁;如果不走索引的话,InnoDB 行锁升级为表锁,性能下降。