天天看点

数据库学习篇之数据库索引深入理解

索引的存储

聚集索引和非聚集索引的存储不相同,那么都是怎么存储的呢?

例如:有一张学生表如下:

create table 'student' (
'id' int(11) not null auto_increment comment '主键id',
'name' varchar(50) not null default '' comment '学生姓名',
'age' int(11) not null default 0 comment '学生年龄',
primary key ('id'),
key 'idx_age' ('age'),
key 'idx_name' ('name')
) ENGINE=InnoDB default charset=utf8 comment ='学生信息';
           

 表中具体数据如下:

数据库学习篇之数据库索引深入理解

id 为主键索引,name和age为非聚集索引

1.聚集索引在磁盘中的存储

数据库学习篇之数据库索引深入理解

聚集索引叶子结点存储是表里面的所有行数据;每个数据页在不同的磁盘上面;

如果要查找id=5的数据,那么先把磁盘0读入内存,然后用二分法查找id=5的数在3和6之间,然后通过指针p1查找到磁盘2的地址,然后将磁盘2读入内存中,用二分查找方式查找到id=5的数据。

2.非聚集索引在磁盘中的存储

数据库学习篇之数据库索引深入理解

叶子结点存储的是聚集索引键,而不存储表里面所有的行数据,所以在查找的时候,只能查找到聚集索引键,再通过聚集索引去表里面查找到数据。

如果要查找到name = 小徐,首先将磁盘0加载到内存中,然后用二分查找的方法查到在指针p1所指的地址上,然后通过指针p1所指的地址可知道在磁盘2上面,然后通过二分查找法得知小徐id=4;

然后在根据id=4将磁盘0加载到内存中,然后通过二分查找的方法查到在指针p1所指的地址上,然后通过指针p1所指的地址可知道在磁盘2上面,然后通过id=4查找出需要的行数据,就查找出name=小徐的数据了。

索引失效的情况---哪些情况sql不走索引

根据上例中的学生信息表,表中两个字段age,name加了索引,索引失效情况具体如下:

key 'idx_age' ('age'),

key 'idx_name' ('name')
           

  1.Like这种就是%在前面的不走索引,在后面的走索引

A:select * from student where'name' like'王%'

B:select * from student where'name' like'%小'

A走索引,B不走索引
           

  2.用索引列进行计算的,不走索引

A:select * from student where age =10+8

B:select * from student where age +8 =18

A走索引,B不走索引
           

 3.对索引列用函数了,不走索引

A:select * from student where  concat('name','哈') ='王哈哈';

B:select * from student where name = concat('王哈','哈');

A不走索引,B走索引
           

 4. 索引列用了!= 不走索引

select * from student where age !=18
           

总的来说,哪些情况索引会失效有以下几种:

(1)条件中用or(这就是为什么少用or的原因)

(2)对于多列(复合、联合)索引,不是使用的第一部分,则不会使用索引。(最左匹配原则或者叫做最左前缀原则)

比如:Index_SoftWareDetail索引包含(a,b,c) 三列,但是查询条件里面,没有a,b 列,只有c 列,那么 Index_SoftWareDetail索引也不起作用。

因此:bc   c   acb bac 都是不行的
           

(3)like的模糊查询以%开头,索引失效

(4)如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不会使用索引

(5)如果MySQL预计使用全表扫描要比使用索引快,则不使用索引

(6)判断索引列是否不等于某个值时,‘!=’操作符

(7)对索引列进行运算。这里运算包括+-*/等运算,也包括使用函数

(8)索引字段进行判空查询时。也就是对索引字段判断是否为NULL时。语句为is null 或is not null。

比如:select * from SoftWareDetailInfo where CreateTime is null 此时就不检索time字段上的索引表了。也就是索引在这条语句执行时失效了。
接着再执行:
select * from SoftWareDetailInfo where CreateTime = '2015-04-11 00:00:00' 此时就会检索索引表了。索引又起作用了。
           

(9)范围列可以用到索引(联合索引必须是最左前缀),但是范围列后面的列无法用到索引

索引的优化

(1)建立联合索引的时候,要把查询频率较高的列放在最左边

(2)如果建立了(a,b)索引,就不必再独立建立a索引。同理如果建立了(a,b,c)联合索引,就不必再独立建立a,(a,b)索引

(3)存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如where a>? and b=?,那么即使 a 的区分度更高,也必须把 b 放在索引的最前列。

(4)最左前缀原则,并不是要求where后的顺序和联合索引的一致。下面的 SQL 语句也可以命中 (login_name, passwd) 这个联合索引。但还是建议 where 后的顺序和联合索引一致,养成好习惯。

select uid, login_time from user where passwd=? and login_name=?
           

(5)尽量不要使用左模糊和全模糊,如果需要可以使用搜索引擎来解决。

(6)union,in和or都可以命中索引,但是建议使用in。

(7)负向条件查询不能使用索引,可以优化为in查询。

负向条件查询有:!=  <>  not in  not like等等

例如:select * from user where status!=1 and status!=2

优化为:select * from user where status in (0,3,4);
           

(8)合理使用联合索引的最左前缀原则,如果在(a,b,c)三个字段上建立联合索引,那么它能够加快 a | (a,b) | (a,b,c) 三组查询速度。

比如说把(username,password)建立了联合索引,因为业务上几乎没有password的单条件查询,而有很多username的单条件查询需求,所以应该建立(username,password)的联合索引,而不要建立(password,username)的联合索引
           

(9)把计算放到业务层而不是数据库层。

(10)表数据比较少、更新十分频繁、数据区分度不高的字段上不宜建立索引。

一般区分度在80%以上的时候就可以建立索引,区分度可以使用 count(distinct(列名))/count(*) 来计算。
           

(11)强制类型转换会全表扫描。

例如:如果phone字段是varchar类型,则下面的sql不能命中索引

select * from user where phone = 18838003017

可以优化为:select * from user where phone = '18838003017'
           

(12)利用覆盖索引进行查询操作,避免回表,提高效率。

select uid,login_time from user where username=? and password=?

如果建立了(username,password,login_time)的联合索引,由于login_time已经建立在索引中了,被查询的username和password就不用去row上获取数据了,从而加速查询
           

(13)在order by和group by中要注意索引的有序性。

如果order by是组合索引的一部分,应该将该字段放在组合索引的最后

例如:where a=? and b=? order by c ->可以建立联合索引(a,b,c)

如果索引中有范围查找,则索引的有序性无法利用

例如:where a>10 order by b ->索引(a,b)无法排序
           

(14)建立索引的列,不许为null。

单列索引不存 null 值,复合索引不存全为 null 的值,如果列允许为 null,可能会得到“不符合预期”的结果集,所以,请使用 not null 约束以及默认值。
           

sql语句的优化

(1)能用到索引尽量用到索引,对索引的优化实际上就是sql语句的调优。

(2)任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

(3)尽量使用where,而不要使用having。

(4)尽量使用多表查询,不要使用子查询。

(5)where后的and,or左右执行顺序不同。

运算符为and时--尽量把为假的放在右边

运算符为or时--尽量把为真的放在右边
           
PS:本篇为网上查找的教程学习整理形成,原文链接找不到了,侵删。