美文网首页
MySQL索引篇

MySQL索引篇

作者: MiniSoulBigBang | 来源:发表于2020-05-03 13:44 被阅读0次

    1 索引介绍

    1.1 索引是什么

    ​ 官方介绍索引是帮助MySQL高效获取数据数据结构。更通俗的说,数据库索引好比是一本书前面的目 录,能加快数据库的查询速度

    1.2 索引的优势和劣势

    1.2.1 索引的优势

    1.2.1.1 检索

    ​ 可以提高数据检索的效率,降低数据库的IO成本,类似于书的目录。

    1.2.1.2 排序

    ​ 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗。

    1.2.1.2.1 where子句中

    ​ 被索引的列会自动进行排序,包括【单列索引】和【组合索引】,只是组合索引的排序要复杂一些。
    ​ 如果按照索引列的顺序进行排序,对于order by语句来说,效率就会提高很多。
    ​ where+索引列,会把条件下推到存储引擎层处理从而加快数据的过滤,这就是索引下推(ICP),5.6以 后的新特征。

    1.2.1.2.2 select子句中

    ​ select +索引字段,会生成覆盖索引,减少检索次数。

    1.2.2 索引的劣势

    ​ 索引会占据磁盘空间。

    ​ 索引虽然会提高查询效率,但是会降低更新表的效率。比如每次对表进行增删改操作,MySQL不仅要保 存数据,还有保存或者更新对应的索引文件。

    2 索引分类

    ​ 单列索引、组合索引、全文索引、空间索引(根本看不到)、位图索引(oracle所特有的)

    3 索引的使用

    3.1 创建索引

    ​ 单列索引之普通索引

    create index index_name on table(column(length));

    alter table table_name add index index_name(column(length));

    ​ 单列索引之唯一索引

    create unique index index_name on table(column(length));

    alter table table_name add unique index index_name(column(length));

    ​ 单列索引之全文索引

    create fulltext index index_name on table(column(length))

    alter table table_name add fulltext index index_name(column(length))

    ​ 组合索引

    alter table table_name add index index_name(column1(length1),column2(length2));

    3.2 删除索引

    drop index index_name on table_name;

    alter table table_name drop index index_name;

    3.3 查看索引

    show index from table_name;

    4 索引原理分析

    4.1 索引存储结构

    ​ 索引是在存储引擎中实现的,也就是说不同的存储引擎,会使用不同的索引。

    ​ MyISAM和InnoDB存储引擎:只支持BTREE索引,也就是说默认使用BTREE,不能够更换。

    ​ MEMORY/HEAP存储引擎:支持HASH和BTREE索引。

    4.2 B树和B+树

    https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

    B树图示

    ​ B树是为了磁盘或其它存储设备而设计的一种多叉(下面你会看到,相对于二叉,B树每个内结点有多个 分支,即多叉)平衡查找树。

    005.png

    ​ B树的高度一般都是在2-4这个高度,树的高度直接影响IO读写的次数。

    ​ 如果是三层树结构,支撑的数据可以达到20G;如果是四层树结构,支撑的数据可以达到几十T。

    B+树图示

    006.png

    B树和B+树的区别

    ​ B树和B+树的最大区别在于非叶子节点是否存储数据的问题。

    ​ B树是非叶子节点和叶子节点都会存储数据。

    ​ B+树只有叶子节点才会存储数据,而且存储的数据都是在一行上,而且这些数据都是有指针指向的,也 就是有顺序的。

    4.3 聚集索引(InnoDB)

    ​ 聚集索引也叫聚簇索引,指的是索引和数据在一个文件上。

    主键索引

    007.png

    ​ 一棵主键索引树上的叶子节点挂载着所有的数据。

    ​ 不建主键的情况下mysql找唯一的字段当主键,如果没有唯一字段,则自动生成伪列当主键。

    ​ 不建主键会影响性能。

    ​ 建表不建议使用uuid,可以使用雪花算法。

    ​ 原因一:uuid无序。

    ​ 原因二:辅助索引挂载的数据(主键)比较大,占用空间。

    辅助索引(次要索引)

    008.png

    ​ 辅助索引下挂载的是主键的值。

    回表

    ​ 在一次的查询过程中,先从辅助索引树上找到主键的值,然后再到主键索引树上找到数据,这个过程称为回表。

    select * from t where id ='1';此过程不涉及回表(主键是id)

    select * from t where name = 'Alice';此过程涉及回表

    select id,name from t where name = 'Alice';此语句形成覆盖索引(只在辅助索引树上进行查找即可返回数据)

    ​ 若想形成覆盖索引,则select的字段要么是where后的索引字段,要么是和where后的字段形成组合索引中的字段,当然也可以将主键和上述两种情况进行组合。

    上述表主键是id,并且name是单列索引。

    4.4 非聚集索引(MyISAM)

    ​ 非聚集索引也叫非聚簇索引,指的是索引和数据不在一个文件上。

    主键索引

    009.png

    ​ 主键索引树上所挂载的是该主键的地址值,可以理解为数据记录的地址值。

    辅助索引(次要索引)

    010.png

    5 索引使用场景

    5.1 哪些情况需要创建索引

    ​ 1、主键自动建立唯一索引。

    ​ 2、频繁作为查询条件(即 where 后面)的字段,应该创建索引。

    ​ 3、查询中排序的字段,应该创建索引,因为 B + tree 有顺序 。

    ​ 4、统计或者分组字段,应该创建索引。

    ​ 5、覆盖索引好处是什么?不需要回表,用到组合索引可形成覆盖索引。

    ​ 6、多表关联查询中,关联字段(即 on 两边)应该创建索引 。

    5.2 哪些情况不需要创建索引

    ​ 1、表记录太少(万级别) 索引是要有存储的开销

    ​ 2、频繁更新 索引要维护

    ​ 3、查询字段使用频率不高

    5.3 为什么使用组合索引

    ​ 由多个字段组成的索引就是组合索引,索引的使用顺序就是创建的顺序

    ​ 使用组合索引的话,在一颗索引树上有多个字段,容易形成覆盖索引,效率高

    索引使用:遵循最左前缀原则

    ​ 1、前缀索引

    ​ like 常量% 使用索引

    ​ like %常量 不使用索引

    ​ 2、最左前缀

    ​ 从左向右匹配直到遇到范围查询 > < between 索引失效

    alter table t1 add index idx_a_b_c_d(a,b,c,d);

    explain select * from t1 where a=1 and b=1 and c=1 and d=1;key_len=20

    explain select * from t1 where a=1 and b=1 and c>1 and d=1;key_len=15

    explain select * from t1 where a=1 and b=1 and d=1 and c>1;key_len=15

    drop index idx_a_b_c_d on t1;

    alter table t1 add index idx_a_b_c_d(a,b,d,c);

    explain select * from t1 where a=1 and b=1 and d=1 and c>1;key_len=20

    6 索引失效

    6.1 查看执行计划

    参数说明

    explain出来的信息有10列,分别是

    id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra

    案例表

     --用户表
     create table tuser(
     id int primary key, 
     loginname varchar(100), 
     name varchar(100),age int,sex char(1),
     dep int,
     address varchar(100) );
     --部门表
     create table tdep( id int primary key, name varchar(100) ); 
     --地址表
     create table taddr( id int primary key, addr varchar(100) );  
    
     --创建普通索引
     mysql> alter table tuser add index idx_dep(dep);
     --创建唯一索引
     mysql> alter table tuser add unique index idx_loginname(loginname); 
     --创建组合索引
     mysql> alter table tuser add index idx_name_age_sex(name,age,sex); 
     --创建全文索引
     mysql> alter table taddr add fulltext ft_addr(addr); 
    

    6.1.1 id

    ​ 每个 select 语句都会自动分配的一个唯一标识符,表示查询中操作表的顺序,有三种情况:

    ​ id相同:执行顺序由上到下

    ​ id不同:如果是子查询,id号会自增,id越大,优先级越高

    ​ id相同的不同的同时存在

    ​ id列为null的就表示这是一个结果集,不需要使用它来进行查询。

    6.1.2 select_type(重要)

    查询类型,主要用于区别普通查询、联合查询 (union 、 union all) 、子查询等复杂查询。

    simple

    ​ 表示不需要union操作或者不包含子查询的简单select查询。

    ​ 有连接查询时,外层的查询为simple,且只有一个。

    mysql> explain select * from tuser;
     id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
     | 1|SIMPLE |tuser|ALL |NULL |NULL|NULL |NULL| 1 | NULL |
    

    primary

    ​ 一个需要union操作或者含有子查询的select,位于最外层的查询为primary。且只有一个

    explain select (select name from tuser) from tuser ;
    | id | select_type | table | type | possible_keys | key | key_len |ref |rows|Extra |  
    | 1|PRIMARY |tuser|index|NULL |idx_dep |5 |NULL| 1|Usingindex|
    | 2 | SUBQUERY | tuser | index | NULL | idx_name_age_sex | 312 |NULL| 1|Usingindex|
    

    subquery

    ​ 除了from字句中包含的子查询外,其他地方出现的子查询都可能是subquery

    explain select * from tuser where  id = (select max(id) from tuser);
    | id | select_type | table | type | possible_keys | key | key_len |ref |rows|Extra |
    | 1|PRIMARY |tuser|const|PRIMARY |PRIMARY|4 |const|1 | NULL|
    | 2|SUBQUERY |NULL |NULL |NULL |NULL |NULL |NULL | NULL | Select tables optimized away |
    

    dependent subquery

    ​ 与dependent union类似,表示这个subquery的查询要受到外部表查询的影响

    explain select id,name,(select name from tdep a where a.id=b.dep) from tuser b;
    | id | select_type | table | type | possible_keys | key | key_len |ref |rows|Extra |
    | 1|PRIMARY |b |ALL |NULL |NULL |NULL | NULL | 2|NULL |
    | 2 | DEPENDENT SUBQUERY | a | eq_ref | PRIMARY | PRIMARY | 4 | demo1.b.dep | 1 | NULL |
    

    union todo

    ​ union连接的两个select查询,第一个查询是PRIMARY,

    ​ 除了第一个表外,第二个以后的表select_type 都是union

    explain select * from tuser where sex='1'  union select * from tuser where sex='2';
    

    dependent union todo

    ​ 与union一样,出现在union 或union all语句中,但是这个查询要受到外部查询的影响

    explain select * from tuser where sex in 
    (select sex from tuser where sex='1' union select sex from tuser where sex='2');
    

    union result

    ​ 包含union的结果集,出现在union和union all语句中。

    ​ 因为它不需要参与查询,所以id字段为null 。

    derived

    ​ from子句中出现的子查询,也叫做派生表,其他数据库中可能叫做内联视图或嵌套select

    explain select * from (select * from tuser where sex='1') b;
    | id | select_type | table | type | possible_keys | key | key_len |ref |rows|Extra |
    | 1|PRIMARY |<derived2>|ALL |NULL |NULL|NULL |NULL| 2 | NULL |
    | 2|DERIVED |tuser |ALL |NULL |NULL|NULL |NULL| 2 | Using where |
    

    6.1.3 table

    ​ 显示的查询表名,如果查询使用了别名,那么这里显示的是别名

    ​ 如果不涉及对数据表的操作,那么这显示为null 。

    ​ 如果显示为尖括号括起来的,就表示这个是临时表,后边的N就是执行计划中的id,表示结果来自于这个查询产生。

    ​ 如果是尖括号括起来的<union M,N>,与上类似,也表示是一个临时表,表示这个结果来自于union查询的id为M,N的结果集。

    6.1.4 type (重要)

    ​ 依次从好到差:

    system, const, eq_ref, ref, fulltext, ref_or_null, 
    unique_subquery, index_subquery, range, index_merge, index, ALL
    

    除了 all 之外,其他的 type 都可以使用到索引;

    除了 index_merge之外,其他的type 只可以用到一个索引,优化器会选用最优索引(一个) 。

    ***索引使用至少要到range级别***
    

    system

    ​ 表中只有一行数据或者是空表。

    explain select * from (select * from tuser where id=1) a;
    | id | select_type | table | type | possible_keys | key | key_len |ref |rows|Extra |
    | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1|NULL |
    | 2|DERIVED |tuser |const |PRIMARY |PRIMARY|4 | const| 1|NULL |
    

    const (重要)

    ​ 使用唯一索引或者主键,返回记录一定是1行记录的等值where条件,通常type是const。其他数据库也叫做唯一索引扫描 。

    explain select * from tuser where id=1;
    explain select * from tuser where loginname = 'zhy';
    

    eq_ref (重要)

    ​ 连接字段为主键或者唯一索引。 此类型通常出现在多表的 join 查询,表示对于前表的每一个结果, 都只能匹配到后表的一行结果,并且查询的比较操作通常是 '=',查询效率较高。

    explain select a.id from tuser a left join tdep b on a.dep=b.id;
    | id | select_type | table | type | possible_keys | key | key_len |ref |rows|Extra |
    | 1|SIMPLE |a |index |NULL |idx_dep|5 |NULL| 2 | Using index |
    | 1|SIMPLE |b |eq_ref|PRIMARY |PRIMARY|4 | demo1.a.dep | 1 | Using index |
    

    ref (重要)

    ​ 针对使用非唯一性索引查询、或者使用等值(=)非主键连接查询、或者是使用最左前缀规则索引查询。

    
    explain select * from tuser where dep=1; -- 非唯一索引
    | id | select_type | table | type | possible_keys | key | key_len |ref |rows|Extra |
    | 1|SIMPLE |tuser|ref |idx_dep |idx_dep|5 |const|1|NULL |
    
    explain select a.id from tuser a left join tdep b on a.name=b.name; -- 等值非主键连接
    | id | select_type | table | type | possible_keys | key | key_len |ref |rows|Extra |
    | 1 | SIMPLE | a | index | NULL | idx_name_age_sex | 312 |NULL | 2|Usingindex |
    | 1|SIMPLE |b |ref |ind_name |ind_name |72| demo1.a.name | 1 | Using where; Using index |
    
    explain select * from tuser where name = 'zhaoyun'; -- 最左前缀
    | id | select_type | table | type | possible_keys | key | key_len |ref |rows|Extra |
    |1| SIMPLE |tuser|ref|idx_name_age_sex|idx_name_age_sex|303|const|1| Using index condition|
    
    思考:explain select * from tuser where sex = '1'; todo
    

    fulltext

    ​ 全文索引检索,要注意,全文索引的优先级很高,若全文索引和普通索引同时存,mysql不管代价,优先选择使用全文索引 。

    explain select * from taddr where match(addr) against('bei');
    | id | select_type | table | type | possible_keys | key | key_len |ref |rows|Extra |
    | 1 | SIMPLE | tuser | fulltext | ft_addr | ft_addr | 0| NULL| 1 | Using where |
    

    ref_or_null

    ​ 与ref方法类似,只是增加了null值的比较,实际场景中用的不多。

    unique_subquery

    ​ todo 。

    index_subquery

    ​ todo。

    range (重要)

        **索引范围扫描**,常见于使用 > 、< 、is null 、between 、in 、like 等运算符的查询中。 
    
    explain select * from tuser where id>1;
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    | 1 | SIMPLE | tuser | range | PRIMARY | PRIMARY |4 | NULL | 1 | Using where |
    
    
    explain select * from tuser where name like 'a%';
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    | 1 | SIMPLE | tuser | range | idx_name_age_sex | idx_name_age_sex | 303 | NULL | 1 | Using index condition |
    
    explain select * from tuser where loginname like 'a%';
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    | 1 | SIMPLE | tuser | range | idx_loginname | idx_loginname | 303 | NULL | 1 | Using index condition |
    

    index_merge

    ​ 表示查询使用了两个以上的索引,最后取交集或者并集。常见于 and 、or 的条件使用了不同的索引。官方排序,这个在 ref_or_null 之后,但是实际上由于要读取多个索引,性能可能大部分情况下都不如 range 。

    index (重要)todo

    条件是出现在索引树中的节点的,可能没有完全匹配索引。

    索引全表扫描,把索引从头到尾扫一遍,常见于使用索引列就可以处理不需要读取数据文件的查询、可以使用索引排序或者分组的查询。

    explain select loginname from tuser; -- 单索引
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
    | 1 | SIMPLE | tuser | index | NULL | idx_loginname | 303 | NULL | 2 | Using index | 
    
    explain select age from tuser; -- 组合索引
    | id | select_type | table | type | possible_keys | key | key_len |ref |rows|Extra |
    | 1 | SIMPLE | tuser | index | NULL | idx_name_age_sex | 312 |NULL| 2|Usingindex|
    
    
    思考 : explain select loginname,age from tuser; -- 覆盖索引
    

    all (重要)

    ​ 这个就是全表扫描数据文件,然后再 server层进行过滤返回符合要求的记录。todo

    mysql> explain select * from tuser; 
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
    | 1|SIMPLE |tuser|ALL |NULL |NULL|NULL |NULL| 2 | NULL | 
    
    思考:回表查询如何使用索引?
    

    6.1.5 possible_keys

    ​ 此次查询中可能选用的索引,一个或多个

    6.1.6 key

    ​ 查询真正使用到的索引,select_type 为 index_merge 时这里可能出现两个以上的索引,其他的 select_type这里只会出现一个。

    6.1.7 key_len

    ​ 用于处理查询的索引长度。如果是单列索引,那就整个索引长度算进去;如果是多列索引,那么查询不一定能使用到所有的列,具体使用到了多少个列的索引,这里就会计算进去。留意下这个列的值,算一下你的多列索引总长度就知道有没有使用到所有的列了。

    ​ 另外,key_len 只计算 where 条件用到的索引长度,而排序和分组就算用到了索引,也不会计算到 key_len中。

    6.1.8 ref

    ​ 如果是使用的常数等值查询,这里会显示const 。

    ​ 如果是连接查询,被驱动表的执行计划里会显示驱动表的关联字段 。

    ​ 如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func

    6.1.9 rows

    ​ 这里是执行计划中估算的扫描行数,不是精确值( InnoDB 不是精确的值,MyISAM 是精确的值,主要原因是InnoDB 里面使用了MVCC并发机制) 。

    6.1.10 extra (重要)

    ​ 这个列包含不适合在其他列中显示但十分重要的额外的信息,这个列可以显示的信息非常多,有几十种,常用的有以下几种。

    使用 not in() 形式子查询或 not exists 运算符的连接查询,这种叫做反连接 ,即,一般连接查询是先查询内表,再查询外表,反连接就是先查询外表,再查询内表。todo

    no tables used

    ​ 不带from字句的查询或者From dual查询

    即,一般连接查询是先查询内表,再查询外表,反连接就是先查询外表,再查询内表。

    using filesort (重要)

    ​ 排序时无法使用到索引时就会出现这个,常见于order by和group by语句中。说明MySQL会使用一个外部的索引排序,而不是按照索引顺序进行读取。 MySQL中无法利用索引完成的排序操作称为“文件排序”

    explain select * from tuser order by address; 
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
    | 1| SIMPLE |tuser|ALL |NULL |NULL|NULL |NULL| 2 | Using filesort | 
    

    using index (重要)

    ​ 查询时不需要回表查询,直接通过索引就可以获取查询的数据。表示相应的 select 查询中使用到了覆盖索引 ( covering index ),避免访问表的数据行,效率不错。

    如果同时出现 Using Where ,说明索引被用来执行查找索引键值;

    如果没有同时出现 Using Where ,表明索引用来读取数据而非执行查找动作。

    explain select name,age,sex from tuser ;
    | id | select_type | table | type | possible_keys | key | key_len |ref | rows | Extra | 
    | 1 | SIMPLE | tuser | index | NULL | idx_name_age_sex | 312 |NULL| 2|Usingindex| 
    

    using temporary

    ​ 表示使用了临时表存储中间结果。 MySQL在对查询结果order by group by时使用临时表。临时表可以是内存临时表磁盘临时表,执行计划中看不出来,需要查看status中的 used_tmp_table、used_tmp_disk_table才能看出来。

    explain select distinct a.id from tuser a,tdep b where a.dep=b.id;
    | id | select_type | table | type | possible_keys | key | key_len |ref | rows | Extra | 
    | 1 | SIMPLE | a |index | PRIMARY,idx_loginname,idx_name_age_sex,idx_dep | idx_dep | 5 | NULL| 2 | Using where; Using index; Using temporary | 
    | 1 |SIMPLE | b | eq_ref | PRIMARY | PRIMARY | 4 | kkb2.a.dep | 1 | Using index; Distinct |
    

    using where (重要)

    ​ 表示存储引擎返回的记录并不是所有的都满足查询条件,需要在 server 层进行过滤。

    explain select * from tuser where address='beijing'; -- 查询条件无索引
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
    | 1|SIMPLE |tuser|ALL |NULL |NULL|NULL |NULL| 2 | Using where | 
    
    explain select * from tuser where age=1; -- 索引失效
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    | 1 | SIMPLE |tuser|ALL |NULL |NULL|NULL |NULL| 2 | Using where | 
    
    explain select * from tuser where id in(1,2); 
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
    | 1 | SIMPLE |tuser|range|PRIMARY |PRIMARY|4 |NULL|2 | Using where |
    
    

    using index condition

    ​ 查询条件中分为限制条件和检查条件。5.6之前,存储引擎只能根据限制条件扫描数据并返回,然后 server 层根据检查条件进行过滤再返回真正符合查询的数据。5.6.x之后支持ICP特性,可以把检查条件也下推到存储引擎层,不符合检查条件和限制条件的数据直接不读取,这样就大大减少了存储引擎扫描的记录数量。

    explain select * from tuser where name='asd';
    | id | select_type | table | type | possible_keys | key | key_len | ref   | rows | Extra |
    | 1 | SIMPLE | tuser | ref | idx_name_age_sex | idx_name_age_sex | 303 | const | 1 | Using index condition |
    

    6.2 索引失效分析

    1. 全值匹配我最爱

    ​ 条件与索引一一对应

    explain select * from tuser where name='zhaoyun' and age=1 and sex='1';
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    | 1 | SIMPLE | tuser | ref | idx_name_age_sex | idx_name_age_sex | 312| const,const,const |    1 | Using index condition 
    

    2. 最佳左前缀法则

    ​ 针对组合索引,如果索引了多个列,要遵守最佳左前缀法则。指的是查询从索引的最左前列开始 并且不跳过索引中的 列。

    explain select * from tuser where age=23; -- 带头大哥死
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    | 1| SIMPLE |tuser|ALL |NULL |NULL|NULL |NULL| 2 | Using where |
    
    explain select * from tuser where name='aa' and sex='1'; -- 中间索引断
    | 1 | SIMPLE | tuser | ref | idx_name_age_sex | idx_name_age_sex | 303 | const | 1 | Using index condition |
      
    explain select * from tuser where name='aa' and sex='1' and age=23; -- 比较
      
    | 1 | SIMPLE | tuser | ref | idx_name_age_sex | idx_name_age_sex | 312 | const,const,const 
    | 1 | Using index condition |
      
    explain select * from tuser where name='aa' and sex=1 and age=23; -- 比较  注意sex的写法
    | 1 | SIMPLE | tuser | ref | idx_name_age_sex | idx_name_age_sex | 308 | const,const | 1 | Using index condition |
    

    3. 在索引上做计算

    ​ 不要进行这些操作:计算、函数、自动/手动类型转换,不然会导致索引失效而转向全表扫描

    explain select * from tuser where loginname='zy';
    | 1 | SIMPLE | tuser | const | idx_loginname | idx_loginname | 303 | const| 1|NULL |
    
    explain select * from tuser where left(loginname,1)='zy';
    | 1 | SIMPLE | tuser | ALL |NULL |NULL|NULL |NULL| 2 | Using where |
    

    4. 范围条件右边的列失效

    ​ 不能继续使用索引中范围:条件( bettween、<、>、in 等)右边的

    explain select * from tuser where name='asd' and age>20 and sex='1';
    | 1 | SIMPLE | tuser | range | idx_name_age_sex | idx_name_age_sex | 308| NULL | 1 | Using index condition |
    

    5. 尽量使用覆盖索引

    ​ 尽量使用覆盖索引(只查询索引的列),也就是查询列和索引列一致,减少 select *

    explain select * from tuser ; 
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    | 1|SIMPLE |tuser|ALL |NULL |NULL|NULL |NULL| 2 | NULL | 
    
    explain select name,loginname from tuser ; 
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
    | 1|SIMPLE |tuser|ALL |NULL |NULL|NULL |NULL| 2 | NULL | 
    
    explain select name,age,sex from tuser ; 
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    | 1 | SIMPLE | tuser | index | NULL | idx_name_age_sex | 312 |NULL| 2|Usingindex| 
    
    explain select loginname  from tuser ;
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    | 1 | SIMPLE | tuser | index | NULL | idx_loginname | 303 | NULL | 2 | Using index |
    
    

    6. 索引字段上不要使用不等

    ​ 索引字段上使用(!= 或者 < >)判断时,会导致索引失效而转向全表扫描 。

    ​ 注:主键索引会使用范围索引,辅助索引会失效

    mysql> explain select * from tuser where loginname='zhy'; 
    | id | select_type | table | type  | possible_keys | key | key_len |ref   | rows | Extra |
    | 1 | SIMPLE | tuser | const | idx_loginname | idx_loginname | 303 | const| 1|NULL | 
    
    mysql> explain select * from tuser where loginname!='zhy'; 
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    | 1|SIMPLE |tuser|ALL |idx_loginname|NULL|NULL |NULL| 1 | Using where | 
    

    7. 主键索引字段上不可以判断 null

    ​ 主键字段上不可以使用 null
    ​ 索引字段上使用 is null 判断时,可使用索引

    explain select * from tuser where name is null; 
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
    | 1 | SIMPLE | tuser | ref | idx_name_age_sex | idx_name_age_sex | 303 | const |  1 | Using index condition |
    
    explain select * from tuser where loginname is  null;
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    | 1 | SIMPLE | tuser | ref | idx_loginname | idx_loginname | 303 |const | 1 | Using index condition |
    
    explain select * from tuser where id is not null; -- 主键非空 不使用索引
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
    | 1|SIMPLE |tuser|ALL |PRIMARY |NULL|NULL |NULL| 2 | Using where | 
    

    8. 索引字段使用 like, 以通配符开头

    ​ 索引字段使用like以通配符开头('%字符串')时,会导致索引失效而转向全表扫描。

    explain select * from tuser where name like 'a%';
    |  1 | SIMPLE | tuser | range | idx_name_age_sex | idx_name_age_sex | 303| NULL | 1 | Using index condition |
    
    explain select * from tuser where name like '%a';
    | 1|SIMPLE |tuser|ALL |NULL |NULL|NULL |NULL| 2 | Using where |
    

    ​ 由结果可知,like 以通配符结束相当于范围查找,索引不会失效。与范围条件 ( bettween、<、>、in 等 ) 不同的是,不会导致右边的索引失效 。

    问题:如何解决 like ‘% 字符串 %’ 时索引失效的问题? 使用覆盖索引可以解决。

    explain select name,age,sex from tuser where name like '%a%'; 
    | id | select_type | table | type | possible_keys | key | key_len |ref |rows|Extra |
    | 1 | SIMPLE | tuser | index | NULL | idx_name_age_sex | 312 | NULL | 2 | Using where; Using index |
    

    9. 索引字段字符串要加单引号

    ​ 索引字段是字符串,但查询时不加单引号会导致索引失效而转向全表扫描。

    explain select * from tuser where name=123;
    | 1 | SIMPLE | tuser | ALL | idx_name_age_sex | NULL | NULL | NULL |2 | Using where |
    

    10. 索引字段不要使用 or

    索引字段使用 or 时,会导致索引失效而转向全表扫描

    explain select * from tuser where name='asd' or age=23;
    | 1 | SIMPLE | tuser | ALL | idx_name_age_sex | NULL | NULL | NULL | 2 | Using where |
    

    6.3 总结 ( 假设index(a,b,c))

    where语句 索引是否被使用
    where a = 3 使用到a
    where a = 3 and b = 4 使用到a,b
    where a = 3 and b = 4 and c = 5 使用到a,b,c
    where b = 4 或者 where b = 4 and c =5 或者 where c =5 未使用到
    where a = 3 and c = 5 使用到a,未使用c,因为b中间断了
    where a = 3 and b > 4 and c = 5 使用到a,b,未使用到c
    where a = 3 and b like 'kk%' and c = 5 使用到a,b,c
    where a = 3 and b like '%kk' and c = 5 使用到a
    where a = 3 and b like '%kk%' and c = 5 使用到a
    where a = 3 and b like 'kk%kk%' and c = 5 使用到a,b,c

    优化口诀总结

    全值匹配我最爱,最左前缀要遵守;

    带头大哥不能死,中间兄弟不能断;

    索引列上少计算,范围之后全失效;

    like百分写最右,覆盖索引不写星;

    不等空值还有or,索引失效要少用。

    相关文章

      网友评论

          本文标题:MySQL索引篇

          本文链接:https://www.haomeiwen.com/subject/cqydghtx.html