索引是为了提高数据查询效率,就像书的目录一样。如下图,索引和数据就是位于存储引擎中:
在等值查询和范围查询场景中性能都特别优秀。但是有序数组索引只适用于静态存储引擎,如果需要更新数据的话就成本太高了。
每个节点的左儿子小于父节点,父节点又小于右儿子。二叉树的搜索效率是最高的,但是实际上大多数的数据库存储并不使用二叉树,因为索引不止存在内存中,还要写道磁盘上。一般使用N叉树,这个N取决于数据块的大小。
数据库发展到今天,跳表、LSM树等数据结构也被应用于引擎设计中,但是数据库底层存储的核心就是基于这些数据模型的。
表都是根据主键顺序以索引的形式存放的,这种存储方式称为索引组织表。InnoDB使用了B+树的索引模型,所以数据都是存储在B+树中的。
索引类型分为主键索引和非主键索引:
基于主键索引和普通索引的查询有什么区别?
- 如果语句是select *fromTwhere ID=500,即主键查询方式,则只需要搜索ID这棵B+树;
- 如果语句是select *fromTwhere k=5,即普通索引查询方式,则需要先搜索k索引树,得到ID
的值为500,再到ID索引树搜索一次。这个过程称为回表。
也就是说,基于非主键索引的查询需要多扫描一棵索引树,所以尽量使用主键查询。
相同磁盘I/O次数下,B+可以查到更多节点。而且B+树叶子采用的是双链表,适合于MySQL中常见的基于范围的顺序查找,而B树无法做到这一点。
太高了。
hash只适合等值查询,不适合范围查询。
有没有什么场合适合使用业务字段直接做主键呢?有的,比如有些业务场景需求是:
1、 只有一个索引
2、 该索引必须是唯一索引
由于没有其他索引,所以也就不用考虑其他索引的叶子节点大小问题。这个时候就要优先将这个索引设置为主键,尽量使用主键查询,避免每次查询需要搜索两棵树。
如果执行的语句是select ID fromTwhere k between 3 and 5,这时只需要查ID的值,而ID的值已经在K索引树上了,因此可以直接提供查询结果,不需要回表。也就是说,在这个查询里面,索引k已经覆盖了我们的查询需求。
覆盖索引是指 SQL 中 query 的所有字段,在索引 B+Tree 的叶子节点上都能找得到的那些索引,从二级索引中查询得到记录,而不需要通过聚簇索引查询获得,可以避免回表的操作。
建立在多列上的索引称为联合索引。
可以看到,不只是索引的全部定义,只要满足最左前缀,就可以利用索引来加速检索。这个最左
前缀可以是联合索引的最左N个字段,也可以是字符串索引的最左M个字符。
考虑索引的复用能力:第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的。
MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
mysql> select * from tuser where name like '张%' and age=10 and ismale=1
这个过程InnoDB并不会去看age的值,只是按顺序把“name第一个字是’张’”的记录一条条取出来回表。因此,需要回表4次。
InnoDB在(name,age)索引内部就判断了age是否等于10,对于不等于10的记录,直接判断并跳过。在这个例子中,只需要对ID4、ID5这两条记录回表取数据判断,就只需要回表2次。
使用前缀索引是为了减小索引字段大小,可以增加一个索引页中存储的索引值,有效提高索引的查询速度。在一些大字符串的字段作为索引时,使用前缀索引可以帮助我们减小索引项的大小。
不过,前缀索引有一定的局限性,例如:
可以建立一个联合索引,即「商品ID、名称、价格」作为一个联合索引。如果索引中存在这些数据,查询将不会再次检索主键索引,从而避免回表。所以,使用覆盖索引的好处就是,不需要查询出包含整行记录的所有信息,也就减少了大量的 I/O 操作。
建表的时候,默认将主键索引设置为自增的。
避免写出索引失效的查询语句,否则这样的查询效率是很低的。