最多见的索引数据结构是B-Tree索引,按照顺序存储数据,因此mysql能够用来作order by和group by操做,应该数据是有序的,因此b-tree也就会将相关的列值都存储在一块儿。最后由于索引中存储实际的列值,某些查询只使用索引就能够完成所有查询。mysql
单一的一列索引sql
如图,一个包含三列值的普通索引树数据结构
不是一种单独的索引类型,而是一种数据存储方式,innoDB的聚簇索引实际上在同一结构中保存了B-Tree索引和数据行,当表有聚簇索引时,它的数据行实际上存放在索引的叶子页中,不是全部的存储引擎都支持聚簇索引。函数
聚簇索引和非聚簇索引对比图:性能
若是一个索引包含或说覆盖全部须要查询的字段的值,成为覆盖索引优化
一、依据where查询条件创建索引ui
eg: select a,b from tb_test where c = ?; idx_c(c) ->正确 select a,b from tb_test where c = ? and b = ? idx_cd(c,d) ->正确
二、根据排序order by ,group by , distinct 字段添加索引编码
eg: select * from tb_test order by a; select a,count(*) from tb_test group by a; idx_a(a) ->正确 select * from tb_test order by a,b; idx_a_b(a,b) ->正确 select * from tb_test order where c = ? by a; idx_c_a(c,a) ->正确
例如,以表user中的a,b,c三个列创建联合索引spa
① 全列匹配: select * from user where a = ? and b =? and c = ?;排序
使用创建索引的三个列的值,精确使用到具体的索引,即便顺序不一样mysql查询优化器会自动调整where语句的顺序(而不是innodb),使之适应索引结构
② 最左前缀匹配: select * from user where a = ? and b =? ;
没有提供彻底的列值,索引是从左起进行连续匹配的,所以也可以使用利用a,b,c三列创建起来的索引。
③ 使用索引精确匹配,中间某个条件未提供。Select * from user where a = ? and c =? ;
虽然a和c都在索引列中,可是由于b不存在,因此没法匹配最左前缀的链接。
解决办法:1,若是有大量的查询经过这种方式进行,能够考虑在a和c列上创建一个联合索引。2,经过填坑的方式,即若是b列上的值很少的话(例如枚举,或者简单的bit类型),经过将sql优化成 select * from user where a = ? and b in(?,?,?……) and c = ? 的方式可以提高一部分的性能。
④ 查询没有使用到索引第一列 select * from user where b = ? and c = ?;
这种状况是不符合最左前缀的,没法使用该索引。
⑤ 匹配字符串前缀状况 select * from user where a = ? and b= ? and c like ‘abc%’;
这种状况符合最左前缀,可使用索引,但若是通配符("_" "%"等)不是出如今末尾,则没法使用。
⑥ 范围查询 select * from user where a > ? and b = ? and c = ?;
这种状况可以使用索引,可是b和c列的索引没法使用到,若是范围查询不是最左前缀或者查询条件中有两个范围列则没法使用。
idx_ab(a,b)为例:
能使用上述索引进行排序的操做是:
order by a; a = 3 order by b; order by a,b; order by a desc ,b desc; a > 5 order by a;
不能使用索引帮助排序的查询
order by b; #没有使用到联合索引的第一个字段 a > 5 order by b; #一旦前缀操做是一个range而非=操做,那么就没法利用到索引, 这里 a>5没法利用索引,二联合索引的第一个字段未利用, 所以 order by b也没法利用索引查询 a in (1,3) order by b; #in里面的值没有创建索引,所以没法利用索引,a未用所以order by b也没法使用 order by a asc, b desc; #这里order by a esc是利用了索引,可是b desc未利用到,由于b要和a排序方式一致才可利用到索引
⑦ 条件中带有函数或者表达式select * from user where a = ? and b = ? and left(c,2) = ‘ba’
虽然和c like ‘ba%’;达到的效果是一致的,可是因为使用了函数,所以没法使用索引。
对于使用了表达式的sql,例如 select * from user where a = ? and b = ? and c -1 =?;没法使用索引。
⑧ 字段类型不匹配,可能会致使没法使用索引 a int(11) ,idx_a(a)
where a = '123' ->错误,可能致使未知的错误,这个跟编码有关系
where a = 123 ->正确
一、字段值的重复程度
如身份证号码基本上不可能重复,所以选择性很是好,而人的名字重复性较低,选择性也不错, 性别(男/女)选择性较差,重复度很是高
二、选择性不好的字段一般不适合建立索引,但也有例外
如:男女比例相仿的表中,性别不适合建立单列索引,若是走索引不如走全表扫描, 由于走索引的I/O开销更大 但若是男女比例极度不平衡,要查询的又是少数方,如:理工学校、IT公司等能够考虑使用索引
三、联合索引中选择性好的字段应该排在前面
select * from tab_a where gender=? and name=? idx_name_gender(name,gender) ->正确
四、联合索引能够为单列、复列查询提供帮助
idx_smp(a,b,c) where a=?; ->正确 where a=? and b=?; ->正确 where a=? and c=?; ->正确 (注:须要MySQL5.6版本以上;在5.5及之前版本,能够对a字段进行索引扫描,但c字段不行 ) where a=? and b=? and c=? ->正确
五、合理建立联合索引,避免冗余
(a),(a,b),(a,b,c) ->不可取 (a,b,c) ->正确,能够覆盖前两个
6,合理使用覆盖索引
对于最核心的SQL,咱们能够考虑使用索引覆盖,查询用户名这种操做频率很是高,而索引里面又存储了字段的值,查询时,name字段的值直接在索引中返回,而不须要回表。
覆盖索引覆盖就是将你要查询的字段和条件字段一块儿创建联合索引,这样的好处是不须要回表获取name字段,IO最小,速度块
select name from tb_user where userid=? key idx_uid_name(userid,name) ->覆盖索引扫描