MySql优化之索引优化

  • Post author:
  • Post category:mysql



索引是数据库优化最重要的手段,当我们遇到数据库性能问题的时候首先想到的就应该是索引优化,我们通过一个例子来看看索引对查询效率的影响究竟有多大:


我们还是沿用上一讲的数据库里面有一百万条数据


:


根据


id


查询


index_test


表看一下它的查询计划:


查询速度很快,因为


id


上有主键索引,这里是索引查询


再来根据


user


字段查询一下:


花了


0.57


秒,


如果在


user


字段上加上索引效果会如何呢?


create index idx_item_user on index_test(user);


再来查询一下


:


所用时间趋近于


0


,是不是很完美,这就是索引的魔力


索引如此神奇我们是不是该好好利用,可惜有时候明明建了索引在查询的时候却不通过索引查询,这又是什么原因呢?下面就来好好研究一下吧。


准备环境


:


创建


tb_seller




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’);


创建一个联合索引


CREATE INDEX idx_seller_name_sta_addr ON tb_seller(NAME,STATUS,address);


如何才能避免索引失效呢


?


  1. 查询的时候使用索引的所有字段进行精确匹配


当按照索引中所有的列进行精确匹配时索引可以被用到。


  1. 最左前缀法则


    :


如果创建多个列的组合索引要遵守最左前缀法则,指的是查询从索引的最左边的列开始,不能跳过索引中的列


,


所以我们在创建组合索引时把


where


子句中出现最频繁的一列放在最左边。


本例中我们建立了组合索引


(NAME,STATUS,ADDRESS),


相当于创建了单列索引


(NAME),


组合索引


(NAME,STATUS),


组合索引


(NAME,STATUS,ADDRESS)


所以在查询时想让索引生效


where


条件只能使用:


Name


Name,Status


Name,Status,Address


这样的组合


其它方式索引不会生效


:


Status,


Status,Address


如果是这种方式


:Name,Address,


则只会用到


Name


的索引,不会用到


Address


的索引


打个比方就好比穿桥洞


Name


是第一个桥洞,


Status


是第二个,


Address


是第三个


我们只能穿过第一个桥洞以后才能穿第二个第三个,不能绕过第一个直接穿第二个,第三个,也不能穿过第一个以后直接跳到第三个。


当然如果是这种方式


:


Status,Name


Status,Name,Address


也是可以用到索引的


为什么呢?不是最左匹配吗?原来


MySql


的查询优化器会会判断究竟以什么顺序查询效率最高,然后根据情况调整查询的顺序,让我们也可以用上索引。


  1. 范围查询右边的列,不能使用索引


比较一下这两个查询


:


第一个查询的


key_len


长度为


813


三个索引都用到了,第二个查询


key_len


长度为


410Name





status


这两个条件走了索引,


address


没有走索引


  1. 不要在索引列上进行运算操作


    ,


    否则不会使用索引


  1. 字符串不加单引号不会使用索引


比较下面两个查询


第一个查询


key_len





410


说明


name





status


都使用了索引,第二个查询


key_len





403


说明


name


使用了索引而


status


没有使用,原因是


mysql


的查询优化器会进行自动类型转换导致索引失效。


  1. 查询的字段尽量都是建立索引的字段,不要用


    select *


第一个查询的


extra





Using where


表示在查找使用索引的情况下,需要回表去查询所需的数据。


第二个查询的


extra





Using where,Using index


表示查找使用了索引,并且要查找的数据都在索引里能找到所以不需要回表查询。





  1. or


    分开的条件


    ,


    如果


    or


    前的条件中的列有索引,后面的列中没有索引,那么涉及的索引都不会被用到。




  1. %


    开头的


    like


    模糊查询索引失效,如果


    %


    在结尾索引不会失效:


这里使用了索引


这里没有使用索引


如何解决这个问题呢?可以通过覆盖索引来解决


可以让我们的查询列都在索引列里


查询的列


sellerid,name


都是索引列,所以即使使用了模糊查询也可以使用索引


  1. 如果


    mysql


    评估使用索引比全表扫描更慢则不会使用索引


先在


address


上创建索引


create index idx_item_address on tb_seller(address);


Address


上建了索引为什么这里没用索引呢?因为这张表中总共十条记录有九条都是北京市,所以用索引去查不如全表扫描快。


再来看一个查询


:


这里用到了索引。


10) in


走索引,


not in


不走索引



版权声明:本文为xiaxiaomao1981原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。