本篇关键词
关键词 | 解释 |
聚簇索引 | 主键索引的叶子节点存的是整行数据,在InnoDB里,主键索引也称为聚簇索引 |
二级索引 | 非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引 |
回表 | 先通过普通查询得到主键,再由主键查询得到具体值的过程 |
覆盖索引 | 普通索引能够查询到的需要的数据,从而不需要再回表查询,那么就是覆盖索引 |
索引下推 | 索引遍历过程中,对索引包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表的次数 |
最左前缀 | 在联合索引时,满足联合索引第一个字段的查询也可以进行索引加速,称为最左前缀 |
1.索引常见模型
哈希表、有序数组和搜索树
1.1 哈希表
哈希表是一种以键-值(key-value)存储数据的结构,我们只要输入待查找的值即key,就可以找到其对应的值即Value。哈希的思路很简单,把放在数组里,用一个哈希函数把key换算成一个确定的位置,然后把 value 放在数组的这个位置。
不可避免地,多个key值经过哈希函数的换算,会出现同一个值的情况。处理这种况的一种方法是,拉出一个链表。
哈希表这种结构适用于只有等值查询的场景,比如Memcached及其他一些NoSQL 引擎。
1.2 有序数组
有序数组在等值查询和范围查询场景中的性能就都非常优秀, 但是有序数组索引只适用于静态存储引擎
1.3 搜索树
查询速度和更新速度都是O(log(N))
2. InnoDB的索引模型
在 InnoDB 中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组
织表。InnoDB 使用了 B+ 树索引模型,数据都是存储在 B+树中的。
对于没有主键的表,InnoDB会默认创建Rowid做主键
假如有一个表: 主键列为 ID ,表中有字段 k,并且在 k 上有索引。
建表语句
mysql> create table T(
id int primary key,
k int not null,
name varchar(16),
index (k))engine=InnoDB;
表中 R1~R5 的 (ID,k) 值分别为 (100,1)、(200,2)、(300,3)、(500,5) 和 (600,6),两棵树
的示例示意图如下。
根据叶子节点的内容,索引类型分为主键索引和非主键索引。
主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引
(clustered index)。
非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引
(secondary index)。
2.1 基于主键索引和普通索引的查询区别:
如果语句是 select * from T where ID=500,即主键查询方式,则只需要搜索 ID 这棵
B+ 树;
如果语句是 select * from T where k=5,即普通索引查询方式,则需要先搜索 k 索引
树,得到 ID 的值为 500,再到 ID 索引树搜索一次。这个过程称为回表。
2.2 覆盖索引
如果执行的语句是select ID from T where k between 3 and 5
,这时只需要查 ID 的
值,而 ID 的值已经在 k 索引树上了,因此可以直接提供查询结果,不需要回表。也就是
说,在这个查询里面,索引 k 已经“覆盖了”我们的查询需求,我们称为覆盖索引
2.3 最左前缀
市民表:
CREATE TABLE `tuser` (
`id` int(11) NOT NULL,
`id_card` varchar(32) DEFAULT NULL,
`name` varchar(32) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`ismale` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `id_card` (`id_card`),
KEY `name_age` (`name`,`age`)
) ENGINE=InnoDB
假如在这个市民表上,查询满足姓”李“的人,同时姓名和年龄是联合索引, 那么查询SQL语句"select * from tuser where name like ‘张%’" 也可以使用上索引进行加速
所以,在建立联合索引时,可以考虑索引内的字段顺序,第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的。, 第二个原则就是考虑空间, 像这种(name, age)的联合索引,使用(name, age) 、 (age)两个索引来满足名称和名称年龄,年龄查询是比(age, name)、(name)这种联合索引要节省空间的
2.4 索引下推
在市民表上,新增一个需求,名字第一个字是张,而且年龄是 10 岁的所有男孩, SQL语句select * from tuser where name like '张 %' and age=10 and ismale=1;
这个时候,使用索引前缀假如能够得到满足条件的记录ID3。在MySQL5.6之前,只能从ID3开始一个个回表。到主键索引上找出数据行,再对比字段值。
而在Mysql5.6之后,引入了索引下推优化,可以在索引遍历过程中,对索引包含的字段先做判断,直接过滤掉不满足条件的记录,减少了回表的次数。
无索引下推,和有索引下推的过程