MySQLInnoDB 引擎中的聚簇索引和非聚簇索引有什么区别?
大约 5 分钟
简短回答
提示
聚簇索引:
索引叶子节点存储的是数据行,可以直接访问完整数据。
每个表只能有一个聚簇索引,通常是主键索引,适合范围查询和排序。
非聚簇索引:
索引叶子节点存储的是数据行的主键和对应的索引列,需通过主键才能访问完整的数据行 。
一个表可以有多个非聚簇索引(称之为非主键索引、辅助索引、二级索引),适用于快速查找特定列的数据。
详细回答
聚簇索引
概念
聚簇索引(Clustered Index)是一个特殊的索引,它将索引项和数据存储在一起,以减少磁盘读取次数。在InnoDB引擎中,聚簇索引是默认的索引类型,并且每个表都必须有一个聚簇索引。
特点
- 聚簇索引的数据和索引存储在一起,因此可以减少磁盘读取次数,提高查询效率。
- InnoDB中的主键默认是聚簇索引,如果表没有定义主键,InnoDB会选择第一个唯一且非空的索引作为聚簇索引。
- 如果表既没有主键也没有合适的唯一索引,InnoDB会自动生成一个隐藏的主键(row_id),作为聚簇索引。
- 数据按照聚簇索引的顺序物理存储在表中,所以一个表只能有一个聚簇索引。
- 聚簇索引的叶子节点保存的是数据行本身。
- 聚簇索引对按主键范围查询的操作非常高效,因为相邻的数据在物理存储上也是相邻的。
- 插入和更新操作可能导致页分裂,因此按主键顺序插入数据效率会更高。
非聚簇索引
概念
非聚簇索引(Non-Clustered Index),也称为二级索引(Secondary Index),是指索引结构和数据分开存储的索引。在InnoDB中,所有非主键索引都是非聚簇索引。
特点
- 非聚簇索引的叶子节点不包含完整的数据记录,而是存储了主键值。
- 使用非聚簇索引查询时,需要先通过索引找到主键值,然后再通过主键值到聚簇索引中查找完整的数据记录,这个过程被称为"回表"。
- 一个表可以有多个非聚簇索引,因为它们只存储索引列和主键值。
- 相比聚簇索引,非聚簇索引需要额外的存储空间。
- 当查询只涉及到索引列和主键时,可以通过"覆盖索引"避免回表操作,提高查询效率。
- 非聚簇索引的维护成本低于聚簇索引,因为页分裂主要发生在聚簇索引中。
实战分析
案例一:索引选择对查询性能的影响
假设有一个用户表,结构如下:
CREATE TABLE users (
id INT PRIMARY KEY,
username VARCHAR(50) UNIQUE,
email VARCHAR(100),
created_at DATETIME,
INDEX idx_email (email),
INDEX idx_created_at (created_at)
);
在这个表中:
id
是主键,对应聚簇索引username
、email
和created_at
上的索引是非聚簇索引
查询场景分析:
SELECT * FROM users WHERE id = 1;
- 直接使用聚簇索引,一次磁盘IO即可获取完整数据。
SELECT * FROM users WHERE email = 'user@example.com';
- 先通过非聚簇索引
idx_email
找到主键值 - 再通过主键值查询聚簇索引获取完整数据(回表)
- 需要两次索引查找
- 先通过非聚簇索引
SELECT id, email FROM users WHERE email = 'user@example.com';
- 通过覆盖索引优化,只需要在
idx_email
索引中查找 - 不需要回表,因为索引包含了查询需要的所有列
- 通过覆盖索引优化,只需要在
案例二:主键设计的影响
考虑两种不同的表设计:
设计A(自增主键):
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
order_no VARCHAR(20) UNIQUE,
customer_id INT,
order_date DATETIME,
INDEX idx_order_no (order_no),
INDEX idx_customer_id (customer_id)
);
设计B(业务字段作主键):
CREATE TABLE orders (
order_no VARCHAR(20) PRIMARY KEY,
customer_id INT,
order_date DATETIME,
INDEX idx_customer_id (customer_id)
);
分析:
- 设计A使用自增主键,插入数据时主键值递增,新数据总是追加到索引末尾,减少了页分裂,提高了写入性能。
- 设计B使用业务字段
order_no
作为主键,随机的主键值会导致频繁的页分裂,降低写入性能。 - 设计A中查询
order_no
需要回表,设计B中直接使用聚簇索引。 - 设计A的主键占用空间小,二级索引叶子节点存储的主键值也小,节省空间。
总结:聚簇索引与非聚簇索引的区别
特性 | 聚簇索引 | 非聚簇索引 |
---|---|---|
数据存储 | 索引和数据存储在一起 | 索引和数据分开存储 |
叶子节点 | 包含完整的数据记录 | 包含主键值 |
数量限制 | 一个表只能有一个 | 一个表可以有多个 |
默认创建 | 主键自动成为聚簇索引 | 非主键索引都是非聚簇索引 |
查询路径 | 直接获取数据 | 需要回表(除非是覆盖索引) |
维护成本 | 较高(页分裂、数据移动) | 较低 |
适用场景 | 主键查询、范围查询 | 单列查询、复合条件查询 |
存储空间 | 不需要额外空间存储主键 | 需要额外空间存储主键 |
IO性能 | 查询通常只需一次IO | 查询通常需要多次IO |
【聚簇索引结构】
提示
待补充