索引篇
1. 索引底层数据结构和算法
1.1 索引概述
索引是一种加快查询和检索数据的数据结构,其本质就可以看作是排好序的数据结构。如果没有走索引,查询数据将会走全表扫描。
索引底层的数据结构有很多种类型,常见的索引数据结构有:B树、B+树、hash、红黑树;再MySQL中,无论是innodb还是m'yisam存储引擎,都是使用的B+树作为索引的结构;
1.1.1 BST二叉树搜索
1.1.2 红黑树(二叉平衡树)
- 定义:红黑树是一种二叉排序树,即满足左子树结点值小于根节点值小于右子树结点值。同时,与普通二叉排序树(BST)相比,红黑树的每个结点还带有颜色属性,颜色只能是红色或黑色。
- 性质:
-
- 根结点是黑色的。
- 叶结点(外部结点、NULL结点、失败结点)均是黑色的。
- 不存在两个相邻的红色结点,即红色结点的父结点和孩子结点均是黑色的。
- 对每个结点,从该结点到任一叶结点的简单路径上,所含黑结点的数目相同。
1.1.3 B树
1.1.4 B+树
B树和B+树的区别:
- B树的所有结点既存放键( key ),也存放数据( data),但是B+树只有叶子节点存放key和data,非叶子节点只存放key。
- B树的叶子节点是独立的;B+树的叶子节点有一条链表指向相邻叶子节点。
- B树的检索过程不稳定,可能还没有到达叶子节点,检索就结束了。但是B+树每次检索数据都要检索到叶子节点,查找比较稳定。
- B树在进行范围查询时,首先找到要查找的下限,然后对B树进行中序遍历,直到找到查找的上限;但是B+树的范围查询只需要对链表进行遍历即可。
1.1.5 hash索引
哈希索引就是采用一定的hash算法,将键值换算成新的hash值,直接映射到对应的槽位上,然后存储在hash表中。(但是要注意hash冲突)
缺点:不支持范围索引,仅能满足=, in
等。
1.2. Innodb&MyISAM在存储结构的区别
在MySQL中,MyISAM引擎和InnoDB引擎都是使用B+树作为索引结构,但是两者实现的方式不太一样。(聚集索引和非聚集索引)。
- MyISAM存储引擎中,B+叶子节点的data存放的是数据记录的地址。在检索数据时,首先按照B+树去检索数据,取出data域的值,然后以data域的值为地址读取相应的数据记录。这里被称为
非聚集索引
。 - Innodb存储引擎中,其数据文件本身就是索引文件。相比MyISAM,索引文件和数据文件是分开的,其数据文件本身就是按照B+树组织的一个索引结构,树节点的data域保存了完整的数据记录。这个key就是表的主键,这被称为
聚簇索引
。
三层的B+树可以至少存储2000w+的数据。超过两千万就建议分库分表。
MyISAM存储引擎中索引文件(MYI, MyISAM Index)和数据文件(MYD, MyISAM Data)是分离的。
MyISAM存储引擎无论是主键索引还是非主键索引,叶子节点存储的都是索引列+数据的地址值。
InnoDB只有一个ibd(Innodb data)文件,数据文件本身就是索引文件。叶子节点直接存放索引和数据,而不是存放索引和地址。
聚集索引 - 叶子结点包含了完整的数据记录,比如Innodb的主键索引就是聚集索引
非聚集索引 - 叶子节点只包含索引和数据地址,比如MyISAM的主键索引就是非聚集索引
1.3. 索引分类
我们可以按照四个角度来分类索引。
- 按「数据结构」分类:B+tree索引、Hash索引、Full-text索引。
- 按「物理存储」分类:聚簇索引(主键索引)、二级索引(辅助索引)。
- 按「字段特性」分类:主键索引、唯一索引、普通索引、前缀索引。
- 按「字段个数」分类:单列索引、联合索引。
1.4 索引为什么使用b+?
1.这种结构胖矮!矮的作用就是减少io操作,因为每次进行行的跳转就会涉及到io操作;胖是一个层级可以存储很多数据,3层机构就可以存储千万级别的数据;
2.所有叶子节点都在同一层;
3.所有叶子节点之间通过双向链表进行连接;
1.5 聚簇索引与普通索引的区别?
聚簇索引:1.非叶子节点存储的是id和页号;2.叶子节点存储的是行数据;
普通索引:1.非叶子节点存储的是id,索引列和页号;2.叶子节点存储的是id和索引列;
1.6 索引的一些现象和行为?
索引覆盖:是指需要查询的字段全都被索引包含;
回表查询:在普通索引的情况下,查询的字段要多于我们的索引字段,此时查找到id后回到聚簇索引在查找行数据;
索引下推:能使用索引的情况下MySQL会尽量使用索引;即使不满足最左前缀原则也可能会使用;5.6之前的版本是根据索引找到一条数据行,检索数据其他的过滤条件;而现在是现在引擎层判断完全部的条件之后再将结果返回;
索引跳跃:MySQL8之后的索引查询,即使不满足最左匹配原则,MySQL可能优化后还是走的索引;
那种情况不会发生索引跳跃:
1.不是单表查询,而是多表关联;
2.select中的字段包含非索引字段;
3.sql中带有group by或者distinct关键字;
1.7 索引失效情况:
不满足最左前缀;
对索引列进行了运算;
mysql认为使用索引效率低于全表扫描;
1.8 索引建立原则:
1.单个表的索引数量不要太多:
因为每个索引对应一个B+树,并且叶子节点存储了索引的全量数据,一旦索引的数量多,那么就会占用大量的磁盘空间;
再查询之前会对索引的成本进行计算,一旦索引过多,计算的次数就多,也可能会浪费性能;
2.经常出现在where后面的字段可以建立索引
3.order by 和group by后面的字段可以建立索引
4.频繁更新的字段不适合建立索引
因为索引要保证按照索引列的值进行排序,所以一旦索引数据字段频繁更新,那么为了保证索引的顺序,就要频繁的挪动索引列再索引中的位置;
5.对于重复值较少的列建立索引;
1.9 最左前缀匹配的原则(联合索引):
再遇到范围查询(如<、>)就会停止匹配,也就是范围查询的字段是可以走索引的,但是范围查询后面的字段是无法使用联合索引的。
注意:对于>=、<=、between and以及like ‘具体值%’这种存在等值条件的范围查询,等值条件的那部分数据是可以走联合索引的,通过explain关键字分析的索引是相当于走的联合索引;
1.10 为什么B+树存储千万级的数据只需要三到四层高度?
InnoDB存储引擎中页的大小为16KB,而一般数据的主键是INT(占用4字节)或者BIGINT类型(占用8字节),指针类型也是4字节或者8字节,也就是说一个页(B+树中的一个节点)中大概存储16KB/(8B+8B)=1000个键值,这里是计算的非叶子节点;
我们再看叶子节点,假设一行数据的大小为1KB,那么高度为3的B+树可以存放1000*1000*16=16000000条记录。
由此我们可以得出高度为3的B+树可以存储千万条记录。
为什么DBA建议InnoDB必须建主键,并且推荐使用整形的自增主键
?
- 为什么DBA建议InnoDB必须建主键:因为InnoDB采用B+树组织数据文件。
- 为什么不建议使用
uuid
作为主键id:
-
- 因为字符串比较按照ascii逐个字符比较,效率较慢。
- uuid占用空间
- 为什么推荐使用自增的主键id:
-
- 页分裂
-- MySQL主键选择
1. 如果指定了主键id,那么就会使用id作为主键
2. 如果没有指定主键id,那么将会使用第一列唯一索引作为主键
3. 如果没有唯一索引,将会生成一个隐藏字段 DB_ROW_ID 作为隐藏主键
2. explain关键字的详细分析
2.1. 2.1 explain执行计划
2.1.1 id
id表示表的执行顺序,有几个select就有几个id。
id列越大,执行优先级越高,id相同就从上到下执行。
2.1.2 select_type
simple
:简单查询,查询不包括子查询和union
explain select * from actor where id = 1;
- primary:复杂查询中最外层的select的
select_type
为primary
- subquery:包含在select中的子查询(
比如:在select关键词后面的查询
),不在from语句中 - derived:包含在
from
字句中的子查询,MySQL会将结果存放在一个临时表,也叫做派生表(derived
)
# 关闭mysql5.7新特性对衍生表的合并操作
set session optimizer_switch = 'derived_merge=off';
# 查看执行计划
explain
select (select 1 from actor where id = 1)
from (select * from film where id = 1) der;
2.1.3. table列
表示explain的一行正在访问哪个表。
当from子句中有子查询时,table列是<derivedN>
,表示当前查询依赖 id = N
的查询,于是先执行id = N
的查询。
2.1.4. type列
这一列表示关联类型或访问类型
,即MySQL决定如何查找表中的行
,查找数据行记录的大概范围。
依次从最优到最差分别为:system > const > eq_ref > ref > range > index > All
- const
const
表示表中最多只有一行匹配的记录,一次查询就可以找到,常用于使用主键索引或唯一索引的字段作为查询条件。
- system
system
:system是const的特例,表中只有一行记录的情况下为system。
explain
select * from (select * from film where id = 1) tmp;
- eq_ref
当连表查询时,前一张表的行在当前这张表中只有一行数据与之对应。是除了system和const之外最好的join方式,一般用于使用主键
或者唯一索引
的字段作为连表条件。
简单的select
查询不会出现这种type
explain
select * from film_actor
left join film on film_actor.film_id = film.id;
- ref
ref
:不是使用主键或者唯一索引,而是使用普通索引作为所有条件,查询结果可能会找到多条符合条件的行。
explain select * from film where name = 'film1';
- range
range表示对索引列范围查询,比如出现在in()
,between,<,>=
等操作中,使用一个索引检索给定范围的值。
explain select * from actor where id > 1;
- index
如果主键索引和二级索引都有全部数据,那么查询全部数据的时候,会走二级索引,因为主键索引叶子节点数据比较多,树太大了。但是如果只有主键索引有全部数据,就会走主键索引
index表示遍历了整颗索引树,一般是扫描整个二级索引,这种扫描不会从索引树的根节点开始快速查找,而是直