(新手友好)MySQL学习笔记(11):索引(前缀索引,聚簇索引,覆盖索引,最左前缀原则,索引设计原则,索引使用原则,索引失效的常见场景)

目录

前缀索引

聚簇索引

覆盖索引(索引覆盖)

最左前缀原则

索引设计原则

索引使用原则

索引失效的常见场景

前缀索引

        索引开头的部分字符,可以大大节约索引空间,提高索引效率。如TEXT数据类型必须使用前缀索引,因为MySQL不允许索引这些列的完整长度。InnoDB索引最大长度为767字节。

        最简单的理解就是在索引表中存储的不是索引字段的完整字段值,而是索引字段的前一部分字段值,比如:

create index In_sname on student(sname(5));

就是在索引表中存储了sname字段的前5个字节,像jackdane,在索引表中就只存储了jackd部分。

        但是前缀索引会降低索引的选择性(不重复的索引值与总行数的比值),唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。所以要选择足够长的前缀保证较高的选择性,同时不能太长(节约空间)。

        可以统计最常见量的数量,在统计前缀的数量,不断调整前缀长度,当两个结果集的数量接近时,就得到比较合适的前缀长度了。

#查询前缀名相同,并且相同的学生数多于50个的,前缀名和对应学生数
select count(*) as cnt, left(sname,5) as pre_sname from student
group by pre_sname
order by cnt desc limit 50; 

#与上边的作用相同,只是前缀名的长度从5字节变成了6字节
select count(*) as cnt, left(sname,6) as pre_sname from student
group by pre_sname
order by cnt desc limit 50; 

聚簇索引

聚簇索引,一种数据存储方式,将数据放在索引的叶子页,索引和数据在同一个B+树上。因为无法同时把数据放在两个地方,所以一个表只有一个聚簇索引。

在InnoDB中,这个索引是主键,如果没有定义主键,InnoDB会选择一个唯一的非空索引替代,没有的话InnoDB会隐式定义一个主键作为聚簇索引。

(新手友好)MySQL学习笔记(11):索引(前缀索引,聚簇索引,覆盖索引,最左前缀原则,索引设计原则,索引使用原则,索引失效的常见场景)_第1张图片

优点:

  • 可以把相关数据保存在一起。
  • 数据访问更快,索引和数据在同一个结构中。

缺点:

  • 插入速度严重依赖插入顺序。
  • 更新聚簇索引列的代价很高。
  • 插入数据或更新主键时可能面临“页分裂”问题。当主键值要求必须将这一行插入到某个已满的页中时,存储引擎会将该页分裂成两个页面来放该行,页分裂会导致表占用更多的磁盘空间。

页分裂问题:

假设一张表原本的B+树为:

(新手友好)MySQL学习笔记(11):索引(前缀索引,聚簇索引,覆盖索引,最左前缀原则,索引设计原则,索引使用原则,索引失效的常见场景)_第2张图片

当我们插入一条id为4的数据后,在这棵已经满了的B+树中就会新开一个叶子页来存储这条数据。

插入后的B+树为:

(新手友好)MySQL学习笔记(11):索引(前缀索引,聚簇索引,覆盖索引,最左前缀原则,索引设计原则,索引使用原则,索引失效的常见场景)_第3张图片

可以看出这样插入就出现了页分裂问题,又因为聚簇索引依赖插入顺序,那么接下来继续插入id为8或9的数据等,索引就不会在树上第二张叶子页中插入,而是继续在后边开新页,这样会导致磁盘空间的浪费。

  • 非聚簇索引需要两次索引查询

覆盖索引(索引覆盖)

查询所需要的数据都可以从索引(索引表)中获取,而不用再去查询数据表中的实际数据,那么这个索引就是一个覆盖索引,可以结合使用索引部分的explain各属性含义中的Extra结合还有使用索引部分的索引表

覆盖索引可以减少树的搜索次数,避免了回表显著提升了查询性能。

create index ename_job on emp(ename,job);
select ename,job from emp;#只查询索引中的索引字段,就会触发索引覆盖
explain select ename,job from emp;#查看使用索引时,Extra的值为Using index 就是触发了索引覆盖

最左前缀原则

创建多列的联合索引时,满足最左前缀原则。例如创建(a,b,c)三列的索引,实际上相当于创建了a,(a,b),(a,b,c)三个索引。

当不需要考虑排序和分组时,将选择性最高的列放在前面。这时索引的作用只用于优化where条件的查找,这样设计可以最快过滤需要的行。

#创建一个联合索引
create index ename_job_sal on emp(ename,job,sal);

#下面几个查询语句尝试判断一下是否使用索引
explain select * from emp where ename = 'smith';
explain select * from emp where job = 'clerk';
explain select * from emp where ename = 'smith' and job = 'clerk';
explain select * from emp where job = 'clerk' and sal =5000;
explain select * from emp where ename = 'smith' and sal 5000;

索引设计原则

  • 为常作为查询条件的字段建立索引:如果某个字段经常用来左查询条件,那么该字段的查询速度会影响整个表的查询速度。因此,为这样的字段建立索引,可以提高整个表的查询速度。
  • 为经常需要排序,分组和联合操作的字段建立索引:经常需要ORDER BY,GROUP BY,DISTINCT和UNION等操作的字段,排序操作会浪费很多时间。如果为其建立索引,可以有效地避免排序操作。
  • 创建唯一性索引:唯一性索引地值是唯一的,可以更快速地通过该索引来确定某条记录。
  • 限制索引的数目:每个索引都需要占用磁盘空间,所以越多,需要地磁盘空间就越大,修改表时,对索引地重构和更新很麻烦。
  • 小表不建议索引(如数量级在百万以内):由于数据较小,查询花费地时间可能比遍历索引地时间还要短,索引可能不会产生优化效果。
  • 尽量使用前缀索引:如果索引的值很长,那么查询的速度会受到影响。
  • 删除不再使用或者很少使用的索引。

索引使用原则

  • 独立的列:索引使用不当会导致索引失效(查询中实际没有使用索引)。如果查询中的列不是独立的,MySQL不会使用索引。独立的列指查询时索引列不能是表达式的一部分,也不能是函数的参数,这两种情况都会导致索引失效。
  • 使用前缀索引:使用前缀索引可以节约索引空间,从而提高索引效率,但是需要平衡索引的选择性。
  • 使用联合索引:使用联合索引可以避免回表,实现覆盖索引,可以减少大量I/O操作。
  • 合适的索引列顺序:创建联合索引时,不同的列顺序会影响索引的性能,通常将选择性高的列放在最前面。
  • 合适的主键:最好选择不会修改的列作为主键,不考虑分库分表的情况最好使用自增主键。

索引失效的常见场景

  1.         1.过滤条件上使用!=或者<和>:当过滤条件使用等值运算时,就可以直接在索引中查找,如果不是等值运算则需要遍历所有的数据,此时索引就会失效。

            2.过滤条件使用 is not null:与不等于一样,如果使用的是is not null,那么就需要进行全部数据的遍历操作,索引失效,但是如果使用的是is null 那么一九是可以使用索引的。

            3.在索引字段上使用函数或进行计算:比如 查询条件是age+1=1,因为计算机不知道进行的计算是什么计算,因此会让age+1计算后再与1比较,导致索引失效。

            4.在使用联合索引时不满足最左前缀原则:比如创建一个联合索引 INDEX(age,name,classid)当使用这个联合索引的时候select * from student where classid=30 and name=‘张三’未出现age,因此不满足最左前缀原则,就无法使用该索引,导致索引失效。

            5.当使用了类型转换也会导致索引失效:我们先创建一个索引 INDEX(name) name的数据类型是varchar(20),当我们查询条件时 name=2000 时,那么 name字段在查询时就会发生类型转换,从varchar(20)转换成int ,从而使索引失效请注意如果发生的类型转换的是 age =‘18‘,那么发生类型转换的是’18‘,不会影响索引的使用。因此使用索引时一定需要保证数据类型是一致的。

            6,在使用范围查询时,联合索引的部分字段失效(where age>18):在使用联合索引时就比如使用这个联合索引INDEX(age,name,classid),select * from student where age=18 and name=‘张三’ and classid=10如果用的都是等值引用,那么就可以重复的利用索引,如果不是等值引用比如select * from student where age=18 and name=‘张三’ and classid>10就会使得classid 的索引失效,但是age和name的索引还能使用,因此建立索引时将需要范围匹配的字段建立在索引的最后面。

            7.在like字段中,如果是%开头则索引失效(where name like "%abc"):因为建立的索引实际上是从整个字符串的第一个开始进行比较排序的,所以使用like的时候,也只能重新开始比较,如果出现’%abc‘,那么查询的就是以abc结尾的数据,就无法使用索引,因为要一个个遍历数据去寻找以abc结尾的数据。

            8.在使用or进行查询时,or前后出现非索引字段时,索引失效:在使用or进行查询操作时,只有or的前后字段都为索引中的字段时,整个查询才会引用索引,一旦or的前后出现任意一个非索引字段,就会使索引失效。

    此外数据库版本或者数据量不同也会影响索引的效果。

你可能感兴趣的:(学习,笔记)