一、InnoDB 数据组织与主键基础
1. 索引组织表(IOT)
InnoDB 存储引擎中,表数据按主键顺序组织存储 ,这种表称为索引组织表(Index Organized Table, IOT),主键索引即聚簇索引,数据和索引存储在一起。
2. InnoDB 逻辑存储结构
表格
| 层级 | 说明 | 大小 |
|---|---|---|
| 表空间(Tablespace) | 最高层级,管理所有段 | - |
| 段(Segment) | 管理多个区,分为数据段、索引段等 | - |
| 区(Extent) | 连续的页集合,避免碎片 | 1MB(固定 64 个页) |
| 页(Page) | InnoDB 最小存储单元,数据存储的基础 | 16KB(默认) |
| 行(Row) | 实际数据行,包含事务 ID、回滚指针、业务字段 | - |
二、主键优化核心
1. 页分裂与页合并
(1)页分裂
- 原理:InnoDB 页按主键顺序存储,当向已满的页乱序插入数据时,需将页分裂为两个,移动一半数据到新页,产生额外 IO,严重影响性能。
- 触发场景:主键乱序插入、UUID 等无序主键插入。
- 影响:导致页空间利用率降低、IO 开销剧增、插入性能下降。
(2)页合并
- 原理:删除数据时仅标记删除,不物理删除;当页中删除数据达到
MERGE_THRESHOLD(默认页大小的 50%)时,InnoDB 会尝试与相邻页合并,优化空间。 - 补充:
MERGE_THRESHOLD可在创建表 / 索引时自定义,调整合并阈值。
2. 主键设计原则
- 满足业务需求的前提下,尽量降低主键长度主键长度越小,索引体积越小,IO 开销越低,缓存命中率越高。
- 优先顺序插入,使用
AUTO_INCREMENT自增主键自增主键保证顺序插入,完全避免页分裂,插入性能最优。 - 禁止使用 UUID、身份证号等无序自然主键无序主键会导致频繁页分裂,严重拖慢插入性能,同时索引体积膨胀。
- 业务操作中,避免修改主键主键修改会触发数据移动、索引重构,产生大量 IO,影响性能。
3. 大批量插入优化
-
问题:批量插入时,普通
INSERT语句性能低下。 -
解决方案:使用
LOAD DATA LOCAL INFILE批量导入,步骤如下:sql
-- 1. 客户端连接时开启本地文件权限 mysql --local-infile -u root -p -- 2. 开启全局本地文件开关 SET GLOBAL local_infile = 1; -- 3. 执行批量导入 LOAD DATA LOCAL INFILE '/root/sql1.log' INTO TABLE `tb_user` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'; -
关键结论:主键顺序插入性能远高于乱序插入。
三、ORDER BY 优化
1. 两种排序方式
表格
| 排序方式 | 说明 | 性能 |
|---|---|---|
Using filesort |
通过索引 / 全表扫描读取数据,在排序缓冲区(sort buffer)中完成排序,所有非索引直接返回的排序都属于此类 | 低(需额外排序) |
Using index |
直接通过有序索引顺序扫描返回有序数据,无需额外排序 | 高(最优) |
2. ORDER BY 优化原则
- 为排序字段建立合适索引,多字段排序遵循最左前缀法则联合索引需从最左列开始,才能命中索引排序,避免 filesort。
- 尽量使用覆盖索引让查询字段全部包含在索引中,避免回表,同时提升排序效率。
- 多字段排序注意升降序一致性 若一个升序、一个降序,需在创建联合索引时指定对应
ASC/DESC,否则无法命中索引。 - filesort 优化:增大排序缓冲区 若无法避免 filesort,大数据量场景下可增大
sort_buffer_size(默认 256KB),减少磁盘排序,提升性能。
四、GROUP BY 优化
1. 核心原理
GROUP BY 本质是先排序再分组,因此优化逻辑与 ORDER BY 高度一致,核心是利用索引避免排序。
2. 优化示例
sql
-- 1. 删除原有联合索引
DROP INDEX idx_user_pro_age_sta ON tb_user;
-- 2. 无索引分组:触发filesort,性能低
EXPLAIN SELECT profession, COUNT(*) FROM tb_user GROUP BY profession;
-- 3. 创建联合索引(遵循最左前缀)
CREATE INDEX idx_user_pro_age_sta ON tb_user(profession, age, status);
-- 4. 有索引分组:直接利用索引有序性,无需排序,性能高
EXPLAIN SELECT profession, COUNT(*) FROM tb_user GROUP BY profession;
EXPLAIN SELECT profession, COUNT(*) FROM tb_user GROUP BY profession, age;
3. 优化原则
- 分组操作可通过索引大幅提升效率
- 分组索引使用严格遵循最左前缀法则
- 优先使用覆盖索引,避免回表,进一步提升性能。
五、与前文索引体系的联动补充
1. 索引设计原则(补充)
- 针对数据量大、查询频繁的表建索引
- 针对
WHERE/ORDER BY/GROUP BY字段建索引 - 选择区分度高的列,优先唯一索引
- 长字符串使用前缀索引
- 优先联合索引,减少单列索引
- 控制索引数量,避免影响增删改
- 索引列尽量设置
NOT NULL约束
2. 索引失效场景(回顾)
- 违反最左前缀法则
- 范围查询右侧列
- 字符串不加引号
- 头部模糊查询
LIKE '%xxx' OR条件中存在无索引列- 索引列参与运算 / 函数
- MySQL 评估全表扫描更快时
