zoukankan      html  css  js  c++  java
  • 05 索引的使用常识(如何编写SQL语句避免索引失效)

    1 索引的使用常识(如何编写SQL语句避免索引失效)

    创建测试表

    --创建表格,主键为sellerid
    create table `tb_seller` (
        `sellerid` varchar (100),
        `name` varchar (100),
        `nickname` varchar (50),
        `password` varchar (60),
        `status` varchar (1),
        `address` varchar (100),
        `createtime` datetime,
        primary key(`sellerid`)
    )engine=innodb default charset=utf8mb4;
    
    --插入多条数据
    insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
    `address`, `createtime`) values('alibaba','阿里巴巴','阿里小
    店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
    insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
    `address`, `createtime`) values('baidu','百度科技有限公司','百度小
    店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
    insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
    `address`, `createtime`) values('huawei','华为科技有限公司','华为小
    店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
    insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
    `address`, `createtime`) values('itcast','传智播客教育科技有限公司','传智播
    客','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
    insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
    `address`, `createtime`) values('itheima','黑马程序员','黑马程序
    员','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
    insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
    `address`, `createtime`) values('luoji','罗技科技有限公司','罗技小
    店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
    insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
    `address`, `createtime`) values('oppo','OPPO科技有限公司','OPPO官方旗舰
    店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');
    insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
    `address`, `createtime`) values('ourpalm','掌趣科技股份有限公司','掌趣小
    店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
    insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
    `address`, `createtime`) values('qiandu','千度科技','千度小
    店','e10adc3949ba59abbe56e057f20f883e','2','北京市','2088-01-01 12:00:00');
    insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
    `address`, `createtime`) values('sina','新浪科技有限公司','新浪官方旗舰
    店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
    insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
    `address`, `createtime`) values('xiaomi','小米科技','小米官方旗舰
    店','e10adc3949ba59abbe56e057f20f883e','1','西安市','2088-01-01 12:00:00');
    insert into `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`,
    `address`, `createtime`) values('yijia','宜家家居','宜家家居旗舰
    店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');
    
    --创建了复合索引,为name,status,addresss三个字段创建索引
    create index idx_seller_name_sta_addr on tb_seller(name,status,address);
    

    1-1 情况1:全值匹配 ,对复合索引中所有列都指定具体值

    explain select * from tb_seller where name='小米科技' and status='1' and address='北京
    市'G;
    

    • 从explain的语句查询的信息可以看到查找类型的方式是ref,表示查找记录的过程中会使用到索引

    1-2 情况2:复合索引遵循最左前缀法则

    基本要求:查询利用索引的时候从索引定义的最左列字段开始匹配,只有成功匹配才会匹配下一个

    create index idx_seller_name_sta_addr on tb_seller(name,status,address);
    

    注意:最左前缀并不是要求where后面的子句的顺序与索引定义一样,而是在匹配索引的时候,按照索引定义的时候字段顺序先查看有没有name字段,然后再查看有没有status字段,依次类推,只有有一个没有检查到,那么后面的字段就都不会检查了。

    --命令0,下面的命令效果都是等同的。
    explain select * from tb_seller where status='1' and address='北京市' and name='小米科技'G;
    explain select * from tb_seller where status='1' and name='小米科技' and address='北京市'G;
    --命令1:违反最左前缀法则,没有以最左边的字段开头,索引失效
    explain select * from tb_seller where status='1' and address='北京市'G;
    --命令2:符合最左原则,但中间跳了一列,那只有最左边索引失效
    explain select * from tb_seller where name='小米科技'and address='北京市'G;
    

    上图是命令1的执行结果,违反最左前缀法则,导致查找类型为all,即扫描整个数据表,索引失效

    图中key_len为403,说明只用了name作为索引

    1-3 情况3:复合索引中如果有字段进行范围查询,那么该字段后面的字段的索引就会失效。

    注意点:如果某个字段使用了范围查询,那么该字段后面的字段匹配要尤为注意

    explain select * from tb_seller where status>'1' and address='北京市' and name='小米科技'G;
    

    上图中从key_len可以看到status使用范围查询,造成后匹配addr字段索引失效

    1-4 情况4:在索引字段上进行运算操作, 将造成该字段索引将失效

    explain select * from tb_seller where substring(name,3,2)='科技'G;
    

    1-5 情况5:字符串不加单引号,造成该字段索引失效。

    失效原因: 没有对字符串加单引号,MySQL的查询优化器,会自动的进行类型转换,造成索引失效(类似于情况4在索引上进行运算)。

    explain select * from tb_seller where status=0 and name='小米科技'G;
    

    可以看到status是字符串,但是由于使用数字作为匹配条件,造成字段失效

    1-6 情况6:尽量使用覆盖索引,避免select * (尽量只查询建立索引的字段,使得索引表中包含这些字段,避免回表查询)

    覆盖索引的概念

    • 尽量使用覆盖索引(只访问索引的查询(索引列完全包含查询列)),减少select * 。

    更通俗的解释:select的数据列从索引中就能够取得,不必从数据表中读取,换句话说查询列要被所使用的索引覆盖在索引表中包含有查询的字段,因此可以从索引表中获得,不需要到原表中取数据

    上图中using index condition表示当前select查询的信息需要回到原表中去获取,原因是因为需要访问数据表中所有的字段,而在索引表中只包含建立索引的字段,因此需要回表查询数据

    上图中查询的字段只有name,在索引表中就可以获取,因此不需要回表查询。从extra信息也可以看出。

    explain之extra字段信息 说明
    using index 使用覆盖索引的时候就会出现
    using where 在查找使用索引的情况下,需要回表去查询所需的数据
    using index condition 查找使用了索引,但是需要回表查询数据
    using index ; using where 查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据

    1-7 情况7:”where 索引字段的条件限制 or 非索引字段的条件限制“情况下,索引字段的索引会失效,不被使用。

    explain select * from tb_seller where name='黑马程序员' or createtime = '2088-01-01
    12:00:00'G;
    

    上图中可以看到name字段是建立了索引,但是createtime字段是没有索引的,通过or语句连接,实际执行并没有去使用name字段建立的索引。

    1-8 情况8:使用模糊查询%的位置不当也会造成索引失效

    --命令1:模糊符号在前会造成索引失效
    explain select * from tb_seller where name like '%科技'G;  
    --命令2:模糊查询符号在后不会造成索引失效
    explain select * from tb_seller where name like '科技%'G;
    --命令3:索引失效
    explain select * from tb_seller where name like '%科技%'G;
    


    高效的模糊匹配查询:使用覆盖索引

    基本思想:使用select语句时,要求查询的字段都已经建立索引。

    --采用下面的写法,那么模糊匹配时就会走覆盖索引
    explain select name from tb_seller where name like '%科技%'G;
    explain select name,status from tb_seller where name like '%科技%'G;
    explain select name,status,address from tb_seller where name like '%科技%'G;
    

    从上图中可以看到,当仅仅查询建立索引的字段时,即便是采用了模糊查询,也能够在查询时以覆盖索引的方式加快查询的速度

    1-9 情况9:某些情况数据库优化器会认为全表扫描比使用索引快。

    --对address字段单独建立索引,然后使用该字段匹配查询
    create index address_index on tb_seller(address); 
    explain select * from tb_seller where address="北京市";      --不走索引
    explain select * from tb_seller where address="西安市";      --走索引
    

    从上图中可以看到:

    • 匹配的字段是“西安市”,使用了索引
    • 匹配的字段是“北京市”,则没有使用索引

    原因在于该字段12条记录中,11个值是北京市,因此数据库服务层的优化器认为比起扫描索引字段,直接去扫描全表更加快,因此此次查询到索引就失效了。

    1-10 情况10:where 条件匹配使用is NULL和is NOT NULL,有时索引会失效。

    原因:与情况9非常类似,数据库的优化器会进行判定进行全表扫描还是使用索引。

    实例:数据库中某个字段的值绝大部分都是null的话且该字段建立索引,实际使用 select * from table where 字段 is NULL, 那么查询时会进行全表扫描而不是索引。

    1-11 情况11:where中使用in进行匹配走索引, 使用not in不会使用索引

    注意: 实际查询时尽量避免使用not in。

    1-12 多个单列索引和一个复合索引优先建立复合索引

    尽量使用复合索引,而少使用单列索引

    当创建复合索引时:一个复合索引实际索引的个数等于所用的字段数查询时每次可以使用复数个索引进行查询

    create index idx_name_sta_address on tb_seller(name, status, address);
    就相当于创建了三个索引 :
    name
    name + status
    name + status + address
    

    创建多个单列索引时

    create index idx_seller_name on tb_seller(name);
    create index idx_seller_status on tb_seller(status);
    create index idx_seller_address on tb_seller(address);
    

    数据库会选择一个最优的索引(辨识度最高索引)来使用,并不会使用全部索引

    2 索引的使用情况查询

    show status like 'Handler_read%';
    show global status like 'Handler_read%';
    

    名称 索引信息
    Handler_read_first 索引中第一条被读的次数。如果较高,表示服务器正执行大量全索引扫描(这个值越低越好。
    Handler_read_key 如果索引正在工作,这个值代表一个行被索引值读的次数,如果值越低,表示索引得到的性能改善不高,因为索引不经常使用(这个值越高越好
    Handler_read_next 按照键顺序读下一行的请求数。如果你用范围约束或如果执行索引扫描来查询索引列该值增加。
    Handler_read_prev 按照键顺序读前一行的请求数。该读方法主要用于优化ORDER BY ... DESC
    Handler_read_rnd 根据固定位置读一行的请求数。如果你正执行大量查询并需要对结果进行排序该值较高。你可能使用了大量需要MySQL扫描整个表的查询或你的连接没有正确使用键。这个值较高,意味着运行效率低,应该建立索引来补救。
    Handler_read_rnd_next 在数据文件中读下一行的请求数。如果你正进行大量的表扫描,该值较高。通常说明你的表索引不正确或写入的查询没有利用索引

    上面表格信息了解即可。

    参考资料

    01 数据库课程
    02 MYSQL之覆盖索引


    20210305

  • 相关阅读:
    CS1704问题汇总
    Android hdpi ldpi mdpi xhdpi xxhdpi适配详解
    Mac开发工具汇总
    Objective-C语法之代码块(block)的使用 (转载)
    iOS学习之Object-C语言属性和点语法(转载收集)
    object-c中疑问
    Android ListVeiw控件(转载整理)
    Android Studio--》Gradle Scripts配置说明
    xutils android studio引用问题
    15 个 Android 通用流行框架大全(转载)
  • 原文地址:https://www.cnblogs.com/kfcuj/p/14487966.html
Copyright © 2011-2022 走看看