1. 索引是什么
- 索引是加速数据查询的辅助数据结构。能加速的原因是"先查目录再取数据"
- 键:索引列上的取值,比如
name = '张三'。 - 值:能定位到那一行的东西,存什么取决于哪一种索引——
- 聚簇索引(主键索引)的叶子节点存的是整行数据,"值"就是那一行本身;
- 非聚簇索引(辅助索引)的叶子节点存的是主键值(整行只有一份,不放在这里),所以拿到主键值后还得回聚簇索引再查一次,这就是回表;
- B+ 树的非叶节点只存键和指向子节点的页号(逐层缩小范围),叶子节点之间再用链指针串成一串。
- 怎样的值能定位:不是"某种特殊的值",而是——① 查询条件要用得上这个索引(等值,或满足最左前缀;用不上索引时,值再规整也只能全表扫描);② 这个值的区分度要够(重复值太多,树里定位到的是一大段,还要顺着叶子链一条条比对)。
- 注意:索引是内模式(存储)层面的东西,所以它的效果和存储引擎绑在一起(见
[[1 数据定义]]§2 那张层次表)。
2. 建索引与索引的分类
-- 语法格式(课件 36 页)CREATE[UNIQUE][CLUSTER]INDEX<索引名>ON<表名>(<列名>[ASC|DESC][,…]);-- 课件 36 页的例子CREATEINDEXidx_nameONs(sname);-- [例7](课件 38 页)为学生-课程库的三个表建索引CREATEUNIQUEINDEXIdx_StusnoONStudent(Sno);-- Student 按学号升序建唯一索引CREATEUNIQUEINDEXIdx_CoucnoONCourse(Cno);-- Course 按课程号升序建唯一索引CREATEUNIQUEINDEXIdx_SCnoONSC(SnoASC,CnoDESC);-- SC 按学号升序、课程号降序建唯一索引-- 课件注:也可以在创建表的时候直接指定CREATETABLEperson(idINTNOTNULLAUTO_INCREMENTPRIMARYKEY,nameVARCHAR(8),INDEXix_person_name(name)-- 建表时带上索引);| 分类角度 | 种类 | 说明 |
|---|---|---|
| 功能逻辑 | 主键索引 | 唯一且非空,每张表 1 个,聚簇索引 |
| 普通索引 | — | |
| 唯一索引 | — | |
| 全文索引 | — | |
| 物理实现方式 | 聚簇索引 | 叶子节点直接放整行数据 |
| 非聚簇索引(二级索引 / 辅助索引) | 需要回表 | |
| 字段个数 | 单一索引 | — |
| 联合索引 | 最左原则:index(a,b,c)支持a;a,b;a,b,c三类查询 |
两种索引的层次关系(CREATE INDEX建出来的一律是非聚簇的二级索引,聚簇索引由主键决定):
- 建索引就一个动词
CREATE INDEX,可带UNIQUE(唯一)和CLUSTER(聚簇)。
3. 回表
-- 表 t(id 主键, name 建了索引, gender, flag),四条数据SELECT*FROMtWHEREname='lisi';-- (1)先通过普通索引(辅助索引)定位到主键值 id = 5-- (2)再通过聚簇索引定位到行记录 ← 这一步就叫回表-- 课件提的两个问题:-- 如何减少回表?-- 如果查询结果是多行记录,是每条记录都回表还是若干次后回表 1 次?- 回表的含义就是B表name查id,那id到A表再查。
- 要点:课件原话"MySQL 不支持单独的聚簇索引,主键索引即聚簇索引",所以例8 的
CREATE CLUSTER INDEX Stusname ON Student(Sname);(课件 39 页)在 MySQL 上会报错,聚簇索引只能通过主键定下来。
4. 删索引(课件 41 页)
-- 语法格式(课件 41 页)DROPINDEX<索引名>ON<表名>;-- [例9] 删除 Student 表的 Stusname 索引DROPINDEXStusnameONStudent;ALTERTABLEstudentDROPINDEXStusname;-- 等价的第二种写法(课件给的)- 总体:一个索引两种删法,效果相同。
- 大思路:课件原话——SQL 标准中没有定义对索引的修改功能,而采用删除 + 重新定义索引的方式实现。所以
[[1 数据定义]]§2 那张"四类对象 × 三个动词"表里,索引的"修改"一栏是空的。 - 要点:删主码索引要写
ALTER TABLE t DROP PRIMARY KEY,删外码要写DROP FOREIGN KEY 名,都不是DROP INDEX。
5. 索引的实现
二叉树 / 平衡二叉树:一个节点一个键 → 树高约 log2(N),每层一次磁盘 IO,太高 B 树: 非叶节点和叶节点都存数据 → 一页里能装的键变少、树变高;范围查询要在树里来回走 B+ 树: 只有叶节点存数据,叶节点之间用链指针连成一串;非叶节点只存键 → 一页装的键多、树矮(IO 少);范围查询顺着叶节点链走 Hash: 按 hash 值直接定位 → 等值查询快,但范围、排序用不了- 总体:索引的物理实现就是这张图里的四种结构,MySQL 的索引用 B+ 树。
- 比较它们的标准是磁盘 IO 次数——数据在磁盘上,一次 IO 读一页;树越矮、一页装的键越多,需要读的页越少。
- 要点(课件 46 页两道课后讨论题的答案):① 不用平衡二叉树,是因为树高太大、IO 次数多;不用 B 树,是因为非叶节点也存数据导致一页装的键变少、树变高,且范围查询效率差。② Hash 索引适合等值查询;B+ 树索引等值和范围都行,还能用于排序。
6. 最左匹配原则,索引命中与速度
CREATEINDEXidx_objONuser(ageASC,heightASC,weightASC);-- ASC 是 Ascending 的缩写,意思是升序,从小到大 是默认值,不写也是升序。-- 联合索引的排序规则:先比 age,age 相同再比 height,height 也相同才比 weightSELECT*FROMuserWHEREage=1ANDheight=2ANDweight=7;-- 走索引:三列全用上SELECT*FROMuserWHEREage=1;-- 走索引:只用第一列SELECT*FROMuserWHEREheight=2ANDweight=7;-- 不走索引:缺最左的 ageSELECT*FROMuserWHEREage>1;-- 范围查询:age 可用,它右边的列不能再定位- 总体:联合索引
index(a,b,c)能被用上的只有a、a,b、a,b,c这三种"最左前缀"。 - 查询速度快慢,一般而言命中索引的快,命中越多越快。
- 注意,
SELECT * FROM user WHERE height=2 AND weight=7;-- 不走索引:缺最左的 age,是完全不命中索引(本质查询树,开头不能断)
7. EXPLAIN 看执行计划
EXPLAINSELECT*FROMSCWHERESno='2024001';-- 课件:查看查询的执行计划(性能分析之必备)SELECTsno,snameFROMuserinfoUSEINDEX(index_list)WHEREuser_id>100;-- 课件:强制指定用某个索引EXPLAN的查询结果如下表:
| 列名 | 描述 | 列名 | 描述 |
|---|---|---|---|
| id | select 查询的序列号,表示查询中执行 select 子句或表的顺序 | key_len | 实际使用的索引长度 |
| select_type | 查询类型 | ref | 使用索引等值查询时,与索引列进行等值匹配的对象信息 |
| table | 表名 | rows | 预估的需要读取的记录数 |
| partitions | 匹配的分区信息 | filtered | 预估记录经过搜索条件过滤后剩余记录条数的百分比 |
| type | 针对单表的访问方法 type(按执行速度):system > const > eq_ref > ref > range > index > all all = 全表扫描 | Extra | 一些额外的信息 |
| possible_keys | 可能用到的索引 | ||
| key | 实际使用的索引,NULL则没用 |
- 总体:
EXPLAIN把一个查询的执行计划摊开:用了哪个索引、扫多少行、用什么访问方法。 - 大思路:调优就看三处——
key是不是 NULL(没用索引)、type是不是all(全表扫描)、rows是不是很大。 - 要点:课件给的
type快慢序要背下来,all是最慢的一档。
8. 其他题目
主观题:索引一定可以提高查询效率吗?如何使用索引才能提高效率?(课件 39 页)
→ 不一定。索引占空间,而且增删改时都要维护索引;数据量小、或列上重复值很多(如性别)时,走索引并不比全表扫描快;查询条件不满足最左前缀、或查询列被函数包住时,索引也用不上。要用得对:建在经常出现在查询条件里的列上、建在区分度高的列上、联合索引按最左前缀的顺序写条件、建完用EXPLAIN验证。
判断题:聚集索引决定了索引所在表的物理存储结构,一张表中只能有一个聚集索引,但是一个聚集索引可以包含很多列。(课件 42 页)
→对。聚簇索引的叶子里放的就是整行数据,行的物理存放顺序由它决定;一张表只能有一个(数据只有一份物理顺序),但可以是多列组成的联合索引。
主观题:唯一键和主键的区别是什么?(课件 43 页)
→ ① 主键非空且唯一,唯一键允许空值(且可以有多个空值);② 一张表只能有一个主键,唯一键可以有多个;③ 主键对应实体完整性、可作外码的参照目标,唯一键属于用户定义完整性;④ 两者都会自动建索引。