在日常MySQL建表中,我们有三种定义主键的方式。不同的主键定义方式,在InnoDB底层B+树聚簇索引的创建时机、磁盘开销、底层逻辑完全不同。
方式一:字段后直接定义主键(内联主键)
sql
CREATE TABLE user(
id INT PRIMARY KEY,
name VARCHAR(20)
);
-
建表语句执行的同时,InnoDB直接以当前主键字段构建聚簇索引B+树 。
-
数据在磁盘物理页中有序连续存储 。
-
后续新增数据直接按照B+树规则插入,不会产生任何表重构。
-
性能最优,是开发中最推荐的方式。
总结
建表 + 聚簇B+树 一次性生成,零额外开销。
方式二:先建表,后续再追加主键
sql
CREATE TABLE user(
id INT,
name VARCHAR(20)
);
ALTER TABLE user ADD PRIMARY KEY(id);
- InnoDB表必须要有聚簇索引。建表无显式主键时,InnoDB按照优先级自动生成临时的聚簇B+树。
无主键时优先级:
自定义主键 > 唯一索引 > 隐藏6字节 row_id
- 执行 alter 新增主键时:
销毁原本的临时B+树,全表数据重新磁盘排序, 基于新的自定义主键,重新完整构建一颗全新的聚簇B+树
- 整个过程会产生大量IO开销,表数据量越大,性能损耗越严重。
4**.删除主键也会触发全表重构。**
总结
数据量大时会产生大量磁盘IO、锁表、性能雪崩,生产环境严禁后期添加主键。
方式三:定义主键 + 额外普通索引/联合索引
sql
CREATE TABLE user(
id INT PRIMARY KEY,
name VARCHAR(20),
INDEX idx_name(name)
);
InnoDB会同时存在两颗及以上B+树:
- 聚簇索引B+树
叶子节点存储完整的整行数据,以主键为排序规则 。
- 二级索引(普通索引)B+树
叶子节点只存储索引列的值 + 对应的主键值 ,不存储整行数据。
- 回表机制
当通过二级索引查询数据时:
先在二级B+树找到对应主键 → 再拿着主键去聚簇B+树中查询完整数据,这个过程叫做回表查询。
复合索引可以不回表,已知 a 找 b ,在(a,b)二级B+树能直接找到 b
索引创建原则
-
比较频繁作为查询条件的字段应该创建索引
-
唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件
3.更新非常频繁的字段不适合作创建索引
- 不会出现在where子句中的字段不该创建索引