在 InnoDB 中,
表 根据主键顺序以B+ 树索引的形式存放的,
这种存储方式的表称为索引组织表
每一个索引, 面对应一棵 B+ 树。
有一个主键列为 ID 的表,在 k 上有索引
id(主键) | K(索引) | name |
---|---|---|
100 | 1 | R1 |
200 | 2 | R2 |
300 | 3 | R3 |
500 | 5 | R5 |
600 | 6 | R6 |
- 主键索引(聚簇索引): 叶子节点存的是整行数据。
- 非主键索引(二级索引): 叶子节点内容是主键的值。
覆盖索引:select ID from T where k between 3 and 5
,只需要查 ID 的值,而 ID 的值已经在 k 索引树上了,因此可以直接提供查询结果,不需要回表
密集索引:文件中的每个搜索码值都对应一个索引值,就是叶子节点保存了整行, innodb的主键就是
稀疏索引:文件只为索引码的某些值建立索引项, 比如 innodb的其他索引只存了键位信息和主键, myisam的所有索引都是
因此:
如果语句是 select * from T where ID=500,主键查询,
则只需要搜索 ID 这棵 B+ 树;
如果语句是 select * from T where k=5 普通索引查询,
要先搜索 k 索引树,得到 ID 的值为 500,
再到 ID 索引树搜索一次。这个过程称为回表。
也就是说,基于非主键索引的查询需要多扫描一棵索引树
主键选择
插入
如果插入新的行 ID 值为 700,则只需要在 R5 的记录后面插入一个新记录。
如果新插入的 ID 值为 400,需要逻辑上挪动后面的数据,空出位置。
而更糟的情况是,如果 R5 所在的数据页已经满了,根据 B+ 树的算法,这时候需要申请一个新的数据页,然后挪动部分数据过去。这个过程称为页分裂。
除了性能外,页分裂操作还影响数据页的利用率。原本放在一个页的数据,现在分到两个页中,整体空间利用率降低大约 50%。
当相邻两个页由于删除了数据,利用率很低之后,会将数据页做合并。合并的过程,可以认为是分裂过程的逆过程。
自动递增主键为什么好?
性能:
系统会获取当前 ID 最大值加 1 作为下一条记录的 ID 值。
正符合了前面提到的递增插入的场景: 每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂, 成本低
空间:
每个非主键索引的叶子节点上都是主键的值。主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。
联合索引
: 如.用户表有
字段 :id, 身份证号,姓名,
建立 (身份证,名字)联合索引, 叶子节点的内容就不止有id ,还有姓名, 如果用身份证号查姓名的次数比较多, 就划算
下图是 (名字,年龄) 联合索引的一个叶子节点
可以看到 里面有 名字,年龄,主键id
如果用名字 查 年龄, id 都是 覆盖索引
最左前缀: 可以是 最左 N 个字段,也可以是 字符串 的最左 M 个字符。
where name like ‘张 %’
能够用上这个索引,查找到第一个符合条件的记录是 ID3
,然后向后遍历,直到不满足条件为止
查询需求在业务中出现的概率不高,也不能让它走全表扫描, 一定要走到索引
有a,b 2个字段需要索引, 有(a,b) 联合索引, 就这样查询条件里面只有 b 的语句,是无法使用 的,不得不维护另外一个索引,加上(b) 。
还要考虑空间
比如上面这个市民表的,name 字段是比 age 字段大的 ,创建一个(name,age) 的联合索引和一个 (age) 的单字段索引。
索引下推
还是上面那个图(名字,年龄)联合索引
select * from tuser where name like '张 %' and age=10 and ismale=1;
用 “张”,找到第一个满足条件的记录 ID3, 看年龄是不是满足, 满足了才会用id去回表
这MySQL 5.6 引入的, 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
网友评论