(三十八)索引

一、索引介绍

索引可以让数据库加快查询速度,但是却会减慢数据的插入速度。因为每次插入一个数据,都要重新处理一次索引。

索引是利用不断缩小查询范围,去除不相关数据来找到目标数据。

索引使用B+树算法,把索引分层次存储,每次加载一个磁盘块进入内存中(根节点),进行比较,找到对应范围内的指针,然后再去加载另一块磁盘块(枝节点),找到所对应的指针,一直找到最底层叶子节点。

               

如上图是一颗B+树,最上层是根节点,中间是枝节点,最下层是叶子节点。现在假设查找数据项28,先把磁盘块1加载到内存中,数据项28跟数据项17和数据项35比较,可以看到28处于中间,那么就可以去除掉小于17和大于35的部分了。然后,也找到了P2,P2指针对应的磁盘块3。再把磁盘块3加载到内存中,28在26和30之间,这样又除去了小于26和大于30的部分。也找到了指针P2,以及P2指示的数据块8,再把磁盘块8加载到内存中,找到了数据块28。

B+树的性质:

  1. 索引字段要尽量的小因为磁盘块的大小是固定的,只有索引字段越小,可以存储的越多。

索引的最左匹配特性B+树搜索数据时,是从左往右比对,然后根据表中字段顺序依次比对的。

索引的两大类型:

#我们可以在创建上述索引的时候,为其指定索引类型,分两类hash类型的索引:查询单条快,范围查询慢

btree类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它)

扫描二维码关注公众号,回复: 5153378 查看本文章

 

#不同的存储引擎支持的索引类型也不一样

InnoDB 支持事务,支持行级别锁定,支持 B-treeFull-text 等索引,不支持 Hash 索引;

MyISAM 不支持事务,支持表级别锁定,支持 B-treeFull-text 等索引,不支持 Hash 索引;

 

二、聚集索引和辅助索引

 

数据库中的索引可以分为聚集索引和辅助索引:

 

(1)聚集索引和辅助索引的相同点是:不管是聚集索引还是辅助索引,其内部都是B+树形式,就是树的高度(或层数)是一样的,叶子结点中存放着所有的数据。

 

(2)聚集索引和辅助索引的不同点是:叶子节点存放的是否是一整行的信息。(聚集索引存放的是一整行,辅助索引存放的是一部分。)

 


1.聚集索引

 

聚集索引就是不可为空且是唯一的一个字段,最好这个字段在内存中所占空间越小越好。

 

聚集索引的好处之一:它对主键的排序查找和范围查找速度非常快,叶子节点的数据就是用户所要查询的数据

 

聚集索引的好处之二:范围查询(range query),即如果要查找主键某一范围内的数据,通过叶子节点的上层中间节点就可以得到页的范围,之后直接读取数据页即可

 

  1. 辅助索引

 

辅助索引的叶子节点中存储的是主键,然后再使用这个主键走一遍“根节点-->枝节点-->叶子结点”的过程。这样做会增加一倍的IO操作,但是相对于从头走到尾那样的查询,并且每次都要操作IO的方式,这样做是完胜的。

 

三、实际操作

   普通索引INDEX:加速查找

唯一索引:

    -主键索引PRIMARY KEY:加速查找+约束(不为空、不能重复)

   -唯一索引UNIQUE:加速查找+约束(不能重复)

 

联合索引:

    -PRIMARY KEY(id,name):联合主键索引

    -UNIQUE(id,name):联合唯一索引

    -INDEX(id,name):联合普通索引

  1. 创建索引

    聚集索引:

(1) 创建表时,直接创建出来:create table t2(id int primary key,name char(10),age int, class int ,index in_age(age));

唯一索引:

(1) 表创建好后,再去添加唯一索引: alter table t2 add unique key un_class(class);

      辅助索引:

(1) 表创建好后,再去添加辅助索引:create index in_name on t2(name);

  1. 删除索引:

聚集索引:

1)alter table t2 drop primary key;

    辅助索引:

(1) drop index in_name on t2;

(2) alter table t2 drop index in_name;

 唯一索引:

(1)alter table t2 drop index un_class;

 

四、正确使用索引

   并不是说我们创建了索引就一定会加快查询速度,若想利用索引达到预想的提高查询速度的效果,我们在添加索引时,必须遵循以下问题

1、 范围问题,或者说条件不明确,条件中出现这些符号或关键字:>、>=、<、<=、!= 、between...and...、like、大于号、小于号

例如:如果写成where id >1 and id <1000000;会发现,随着你范围的增大,速度会越来越慢,会成倍的体现出来。

2、 不等于!=,比等于要多费时间。

3、 between ...and... 和大于小于一样,范围越小查询速度越快。

4、 like匹配的值里如果后面没有%,那么和=是一样的,速度很快。如果有%,那么速度也很快,会去迅速匹配相应的记录,但是如果%在开始位置,就相当于这个值会与每行记录进行比较一下。所以%在开始位置时,会使查询速度变慢。

5、 =和in可以乱序:

比如a = 1 and b = 2 and c = 3 ,a,b,c的位置可以随意排;

in (b,c,a)in后面的a,b,c也是可以打乱顺序的。

6、 索引列不能参与计算,保持列“干净”:

where id = 1000;这样的条件,可以很容易的对比出来,然后取出这一行的记录。

但是,where id*3 = 10000,这样的计算就会导致每次对比前都得先计算一下id的值是多少,因此就不能发挥出索引快速查询的作用了。

把上面的条件写成 where id = 3000/3;你会发现速度变得很快,因为等于号后面的数字,是在比较之前就计算出来了,不需要每次都计算一次每次都计算一次了,跟直接等于一个常数是一样的,所以很快。结论是不要让你的索引字段参与到计算中。

7、 and和or

a=2 and b>3 and c=4,像这样and的工作原理是,先按照最左原则,从左往右先找出区分度高的把范围缩小,然后再去查找其他的条件。

        区分度=count(distinct 字段)/count(*)  值越大,区分度越高,值越小,区分度越低。

            a=2 or b>3 or c=4,像这样or的工作原理是,按照最左原则,先判断第一个条件是否成立,如果成立那么就停止,不成立的话,再对比第二个,这样依次比较下去。

8、  最左前缀匹配原则,非常重要的原则,对于组合索引mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配(指的是范围大了,有索引速度也慢),比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

9、 其他情况

- 使用函数

select * from tb1 where reverse(email) = 'egon';

- 类型不一致

    如果列是字符串类型,传入条件时必须用引号引起来,不然报错: Unknown column 'xx' in 'where clause'

    select * from tb1 where email = 999;

    #排序条件为索引,则select字段必须也是索引字段,否则无法命中

- order by

    select name from s1 order by email desc;

    当根据索引排序时候,select查询的字段如果不是索引,则速度仍然很慢。这与B+树有关,普通索引的叶子节点中只是保存了聚集索引的值,所以会回表。

    select email from s1 order by email desc;

    特别的:如果对主键排序,则还是速度很快:

        select * from tb1 order by nid desc;

    因为在B+树的叶子节点中,存储的是那一行记录的索引,找到了主键索引就找到了行记录。

 - 组合索引最左前缀

    如果组合索引为:(name,email)

    name and email       -- 命中索引

    name                 -- 命中索引

    email                -- 未命中索引

 

- count(1)或count(列)代替count(*)在mysql中没有差别了

- create index xxxx  on tb(title(19)) #text类型,必须制定长度。

  1. 注意事项

- 避免使用select *

- count(1)或count(列) 代替 count(*)

- 创建表时尽量时 char 代替 varchar

- 表的字段顺序固定长度的字段优先

- 组合索引代替多个单列索引(经常使用多个条件查询时)

- 尽量使用短索引

- 使用连接(JOIN)来代替子查询(Sub-Queries)

- 连表时注意条件类型需一致- 索引散列值(重复少)不适合建索引,例:性别不适合

五、联合索引和覆盖索引

1、联合索引

mysql> create table t(
    -> a int,
    -> b int,
    -> primary key(a),
    -> key idx_a_b(a,b)
    -> );

                                                     

那么何时需要使用联合索引呢?在讨论这个问题之前,先来看一下联合索引内部的结果。从本质上来说,联合索引就是一棵B+树,不同的是联合索引的键值得数量不是1,而是>=2。接着来讨论两个整型列组成的联合索引,假定两个键值得名称分别为a、b如图:

                                                                        

可以看到这与我们之前看到的单个键的B+树并没有什么不同,键值都是排序的,通过叶子结点可以逻辑上顺序地读出所有数据,就上面的例子来说,即(1,1),(1,2),(2,1),(2,4),(3,1),(3,2),数据按(a,b)的顺序进行了存放。

  因此,对于查询select * from table where a=xxx and b=xxx, 显然是可以使用(a,b) 这个联合索引的,对于单个列a的查询select * from table where a=xxx,也是可以使用(a,b)这个索引的。

但对于b列的查询select * from table where b=xxx,则不可以使用(a,b) 索引,其实你不难发现原因,叶子节点上b的值为1、2、1、4、1、2显然不是排序的,因此对于b列的查询使用不到(a,b) 索引。

注意建立联合索引的一个原则:索引是有个最左匹配的原则的,所以建联合索引的时候,将区分度高的放在最左边,依次排下来,范围查询的条件尽可能的往后边放。

   联合索引的第二个好处是在第一个键相同的情况下,已经对第二个键进行了排序处理,例如在很多情况下应用程序都需要查询某个用户的购物情况,并按照时间进行排序,最后取出最近三次的购买记录,这时使用联合索引可以帮我们避免多一次的排序操作,因为索引本身在叶子节点已经排序了。

2.覆盖索引

InnoDB存储引擎支持覆盖索引(covering index,或称索引覆盖),即从辅助索引中就可以得到查询记录,而不需要查询聚集索引中的记录。

使用覆盖索引的一个好处是:辅助索引不包含整行记录的所有信息,故其大小要远小于聚集索引,因此可以减少大量的IO操作。

 

猜你喜欢

转载自www.cnblogs.com/asia-yang/p/10356268.html