索引

除了数据之外,数据库系统还维护为满足特定查找算法的数据结构,这些数据结构以某种方式引用数据.这种数据结构就是索引

实现的数据结构:

mysql 主要提供两种索引类型

  • B-Tree索引
    底层是基于B+Tree实现
    B树索引具有范围查找和前缀查找的能力,对于有N节点的B树,检索一条记录的复杂度为O(LogN)。相当于二分查找。

  • Hash索引
    哈希索引只能做等于查找,但是无论多大的Hash表,查找复杂度都是O(1)。

显然,如果值的差异性大,并且以等值查找(=、 <、>、in)为主,Hash索引是更高效的选择,它有O(1)的查找复杂度。

如果值的差异性相对较差,并且以范围查找为主,B树是更好的选择,它支持范围查找。

索引类型:

聚簇索引、非聚簇索引

  • 聚簇索引

* 如果表设置了主键,则主键就是聚簇索引
* 如果表没有主键,则会默认第一个NOT NULL,且唯一(UNIQUE)的列作为聚簇索引
* 以上都没有,则会默认创建一个隐藏的row_id作为聚簇索引

InnoDB的聚簇索引的叶子节点存储的是行记录(其实是页结构,一个页包含多行数据),InnoDB必须要有至少一个聚簇索引。
由此可见,使用聚簇索引查询会很快,因为可以直接定位到行记录。


  • 普通索引

普通索引也叫二级索引,除聚簇索引外的索引,即非聚簇索引。
InnoDB的普通索引叶子节点存储的是主键(聚簇索引)的值,而MyISAM的普通索引存储的是记录指针。



回表查询

先通过普通索引的值定位聚簇索引值,再通过聚簇索引的值定位行记录数据,需要扫描两次索引B+树,它的性能较扫一遍索引树更低。


索引覆盖:

只需要在一颗索引树上就能获取sql所需要的所有列数据,无需回表,速度更快。

explain的输出结果Extra字段为Using index 时,表示触发了索引覆盖,没有回表查询。


如何实现覆盖索引:

常见的方法是:将被查询的字段,建立到组合索引里去

比如:

user表有 id、name、age 三个字段,id为主键(聚簇索引),name为普通索引

查询

select id,name,age from user where name ='张三'

此时由于name是普通索引,所以会从name的索引树上面找到id的值,再去根据id的值扫描聚簇索引找到对应的数据,此时就出现了回表查询。

为name和age创建一个索引树:

create INDEX idx_age_name ON user(name,age)

再次查询使用age做条件(或附带age做条件)就能出发组合索引

select id,name,age from user where age > 20

此时字段age和name是组合索引idx_age_name,查询的字段id、age、name的值刚刚都在索引树上,只需扫描一次组合索引B+树即可,这就是实现了索引覆盖,此时的Extra字段为Using index表示使用了索引覆盖。


索引覆盖优化limit

当mysql 数据量过大时,此时即使有索引,直接使用limit 分页查询,偏移量过大时速度也会很慢

--此时limit 跨度为600w条数据,查询时间为44s
SELECT * FROM `sys_user` LIMIT 6000000,10;

优化方式1:

SELECT
    * 
FROM
    `sys_user` 
WHERE
    id >= ( SELECT id FROM sys_user LIMIT 6000000, 1 ) 
    LIMIT 10

优化方式2:

SELECT
    * 
FROM
    `sys_user` a
    JOIN ( SELECT id FROM sys_user LIMIT 6000000, 10 ) b USING ( id )

第二种方式比第一种方式快一点


索引失效的情况:

  1. 如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因),要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引
  2. 使用索引字段like查询的时候,用%开头
  3. 对索引字段进行函数计算
  4. where出来的数据超过全表的30%(大概),就不会走索引,mysql会认为这次的效率不如直接全表查(如果mysql估计使用全表扫描要比使用索引快,则不使用索引)

排序字段没有索引,也会很慢


二叉树