聚集索引、辅助索引、覆盖索引、联合索引

本文深入解析了数据库中的各种索引类型,包括聚集索引、辅助索引、覆盖索引和联合索引的工作原理及其应用场景。

转载自   聚集索引、辅助索引、覆盖索引、联合索引

聚集索引(Clustered Index)

聚集索引就是按照每张表的主键构造一棵B+树,同时叶子节点中存放的即为整张表的行记录数据。

举个例子,直观感受下聚集索引。

创建表t,并以人为的方式让每个页只能存放两个行记录(不清楚怎么人为控制每页只存放两个行记录):

 

最后《MySQL技术内幕》的作者通过分析工具得到这棵聚集索引树的大致构造如下: 

聚集索引的叶子节点称为数据页,每个数据页通过一个双向链表来进行链接,而且数据页按照主键的顺序进行排列。

如图所示,每个数据页上存放的是完整的行记录,而在非数据页的索引页中,存放的仅仅是键值及指向数据页的偏移量,而不是一个完整的行记录。

如果定义了主键,InnoDB会自动使用主键来创建聚集索引。如果没有定义主键,InnoDB会选择一个唯一的非空索引代替主键。如果没有唯一的非空索引,InnoDB会隐式定义一个主键来作为聚集索引。


辅助索引(Secondary Index)

辅助索引,也叫非聚集索引。和聚集索引相比,叶子节点中并不包含行记录的全部数据。叶子节点除了包含键值以外,每个叶子节点的索引行还包含了一个书签(bookmark),该书签用来告诉InnoDB哪里可以找到与索引相对应的行数据。

还是以《MySQL技术内幕》中的例子,来直观感受下辅助索引的模样。

还是以上面的表t为例,在列c上创建非聚集索引:

 

然后作者通过分析工作得到辅助索引和聚集索引的关系图:

 

可以看到辅助索引idx_c的叶子节点中包含了列c的值和主键的值。

以Key为7fffffff为例,7是0111,0代表负数,真实的值应该取反加1,是-1,这是列c的值。Pointer是80000001,8是1000,1代表正数,所以80000001代表1,是主键的值。


覆盖索引(Covering index)

InnoDB存储引擎支持覆盖索引,即从辅助索引中就可以得到查询的记录,而不需要查询聚集索引中的记录。

使用覆盖索引有啥好处?

  • 可以减少大量的IO操作

上图中我们知道,如果要查询辅助索引中不含有的字段,得先遍历辅助索引,再遍历聚集索引,而如果要查询的字段值在辅助索引上就有,就不用再查聚集索引了,这显然会减少IO操作。

比如上图中,以下sql可以直接使用辅助索引,

select a from where c = -2;
  • 有助于统计

    假设存在如下表:

  CREATE TABLE `student` (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) NOT NULL,
  `age` varchar(255) NOT NULL,
  `school` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`),
  KEY `idx_school_age` (`school`,`age`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

如果在该表上执行:

select count(*) from student

优化器会怎么处理?

遍历聚集索引和辅助索引都可以统计出结果,但辅助索引要远小于聚集索引,所以优化器会选择辅助索引来统计。执行explain命令:

 

key和Extra显示使用了idx_name这个辅助索引。

还有,假设执行以下sql:

select *  from student where age > 10 and age < 15

因为联合索引idx_school_age的字段顺序是先school再age,按照age做条件查询,通常不走索引: 

但是,如果保持条件不变,查询所有字段改为查询条目数:

select count(*) from student where age > 10 and age < 15

优化器会选择这个联合索引: 


联合索引

联合索引是指对表上的多个列进行索引。

以下为创建联合索引idx_a_b的示例:

 

联合索引的内部结构:

联合索引也是一棵B+树,其键值数量大于等于2。键值都是排序的,通过叶子节点可以逻辑上顺序的读出所有数据。数据(1,1)(1,2)(2,1)(2,4)(3,1)(3,2)是按照(a,b)先比较a再比较b的顺序排列。

基于上面的结构,对于以下查询显然是可以使用(a,b)这个联合索引的:

select * from table where a=xxx and b=xxx ;

select * from table where a=xxx;

但是对于下面的sql是不能使用这个联合索引的,因为叶子节点的b值,1,2,1,4,1,2显然不是排序的。

select * from table where b=xxx

联合索引的第二个好处是对第二个键值已经做了排序。举个例子:

create table buy_log(
    userid int not null,
    buy_date DATE
)ENGINE=InnoDB;

insert into buy_log values(1, '2009-01-01');
insert into buy_log values(2, '2009-02-01');

alter table buy_log add key(userid);
alter table buy_log add key(userid, buy_date);

当执行

select * from buy_log where user_id = 2;

时,优化器会选择key(userid);但是当执行以下sql:

select * from buy_log where user_id = 2 order by buy_date desc;

时,优化器会选择key(userid, buy_date),因为buy_date是在userid排序的基础上做的排序。

如果把key(userid,buy_date)删除掉,再执行:

select * from buy_log where user_id = 2 order by buy_date desc;

优化器会选择key(userid),但是对查询出来的结果会进行一次filesort,即按照buy_date重新排下序。所以联合索引的好处在于可以避免filesort排序。

<think>嗯,用户想了解MySQL聚集索引的原理、特点和使用方法。从引用内容看,用户提供的资料已经覆盖了核心概念,但需要系统化整理。 用户可能正在学习数据库优化,或是遇到实际性能问题。引用[1][4][5]特别重要,它们解释了聚集索引的物理存储特性和查询过程。需要注意纠正引用[2]的一个误区:MySQL的InnoDB实际上只允许主键作为聚集索引,不支持自定义聚集索引。 关于原理部分,要突出B+树结构和"索引即数据"的特性。引用[5]描述的二次查找过程很关键,这能解释为什么推荐使用主键查询。特点方面,用户可能最关心性能影响,需要强调顺序I/O优势和大数据量场景。 使用方法上,用户似乎混淆了主键索引聚集索引的概念(引用[3][4]),这里要明确:在InnoDB中两者是等价的。还要提醒自增主键避免页分裂的问题,这是实际开发中容易忽略的优化点。 用户没有提到具体业务场景,但引用[1]最后提到索引选择性高的列才适合建索引,这个注意事项一定要包含。另外考虑到用户引用了多篇技术文章,可能对底层实现有兴趣,可以适当解释B+树的高度平衡特性如何支撑快速查询。 最后生成的延伸问题要覆盖原理(B+树)、实践(碎片处理)、对比(与非聚集索引联动)三个层面。特别要加入"页分裂"这个技术点,因为引用里没提但实际影响很大。 </think>以下基于引用内容整理的MySQL聚集索引核心知识,包含原理、特点及使用方法: --- ### **一、聚集索引原理** 1. **数据结构** 聚集索引在InnoDB中本质是一棵**B+树**,其叶子节点直接存储**完整行数据**(而非指针)。主键值作为索引键,叶子节点按主键顺序物理存储[^4][^5]。 *示例存储结构*: ``` B+树叶子节点: [主键值, 行数据1] -> [主键值, 行数据2] -> ...(物理有序) ``` 2. **查询过程** - 按主键查询:直接遍历B+树即可获取数据(一次检索)[^5]。 - 按非聚集索引查询:先通过辅助索引找到主键,再回表到聚集索引中查找行数据(二次检索)[^5]。 --- ### **二、核心特点** 1. **物理有序性** 表数据严格按照主键顺序物理存储,类似电话簿按姓氏排序[^2][^4]。 ✅ **优势**:范围查询(如`ID>100`)效率高,因相邻数据物理连续。 ❌ **劣势**:非主键列的插入/删除可能引发**页分裂与碎片**。 2. **唯一性与数量限制** - 每张表**仅有一个**聚集索引(InnoDB强制要求)[^2][^4]。 - 聚集索引**必须唯一**,通常由主键实现(若未定义主键,InnoDB会自动生成隐藏的ROWID)[^3][^4]。 3. **性能影响** - 聚集索引的键值不宜过大,否则会显著增大非聚集索引的占用空间(因非聚集索引需存储主键值)[^5]。 - 频繁更新的列不适合作聚集索引,易引发页分裂。 --- ### **三、使用方法** 1. **创建方式** 通过定义**主键(PRIMARY KEY)** 隐式创建: ```sql CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, -- 聚集索引 name VARCHAR(50) ); ``` 2. **最佳实践** - **优先使用自增主键**:避免随机写入导致的页分裂[^4][^5]。 - **避免修改主键**:可能触发数据物理位置重组。 - **联合主键谨慎使用**:InnoDB不支持联合聚集索引,但可将多列设为主键(此时按主键组合排序)[^2][^4]。 3. **适用场景** - 需要频繁范围查询(如`BETWEEN`、`>`)或排序(`ORDER BY`)的列[^1][^5]。 - 表数据量大且访问模式以主键查询为主(如OLTP系统)[^1]。 --- ### **四、注意事项** 1. **与非聚集索引区别** | **特性** | 聚集索引 | 非聚集索引 | |------------------|------------------|------------------| | 数据存储位置 | 叶子节点存行数据 | 叶子节点存主键值 | | 数量限制 | 每表仅1个 | 可创建多个 | | 物理顺序 | 与索引一致 | 独立于数据存储 | 2. **碎片处理** 定期执行`OPTIMIZE TABLE`重建表,减少页分裂产生的碎片[^1]。 --- ### **延伸问题** 1. 为什么InnoDB必须要求有聚集索引? 2. 如何通过覆盖索引避免回表查询? 3. 页分裂对聚集索引的性能影响如何量化? 4. MyISAM与InnoDB的索引实现有何本质区别? > 引用提示:聚集索引物理存储结构[^4],查询过程二次检索[^5],表仅有一个聚集索引[^2][^4],索引碎片优化[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值