索引是数据库优化最重要的手段,当我们遇到数据库性能问题的时候首先想到的就应该是索引优化,我们通过一个例子来看看索引对查询效率的影响究竟有多大:
我们还是沿用上一讲的数据库里面有一百万条数据
:
根据
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);
如何才能避免索引失效呢
?
-
查询的时候使用索引的所有字段进行精确匹配
当按照索引中所有的列进行精确匹配时索引可以被用到。
-
最左前缀法则
:
如果创建多个列的组合索引要遵守最左前缀法则,指的是查询从索引的最左边的列开始,不能跳过索引中的列
,
所以我们在创建组合索引时把
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
的查询优化器会会判断究竟以什么顺序查询效率最高,然后根据情况调整查询的顺序,让我们也可以用上索引。
-
范围查询右边的列,不能使用索引
比较一下这两个查询
:
第一个查询的
key_len
长度为
813
三个索引都用到了,第二个查询
key_len
长度为
410Name
和
status
这两个条件走了索引,
address
没有走索引
-
不要在索引列上进行运算操作
,
否则不会使用索引
-
字符串不加单引号不会使用索引
比较下面两个查询
第一个查询
key_len
为
410
说明
name
和
status
都使用了索引,第二个查询
key_len
为
403
说明
name
使用了索引而
status
没有使用,原因是
mysql
的查询优化器会进行自动类型转换导致索引失效。
-
查询的字段尽量都是建立索引的字段,不要用
select *
第一个查询的
extra
为
Using where
表示在查找使用索引的情况下,需要回表去查询所需的数据。
第二个查询的
extra
为
Using where,Using index
表示查找使用了索引,并且要查找的数据都在索引里能找到所以不需要回表查询。
-
用
or
分开的条件
,
如果
or
前的条件中的列有索引,后面的列中没有索引,那么涉及的索引都不会被用到。
-
以
%
开头的
like
模糊查询索引失效,如果
%
在结尾索引不会失效:
这里使用了索引
这里没有使用索引
如何解决这个问题呢?可以通过覆盖索引来解决
可以让我们的查询列都在索引列里
查询的列
sellerid,name
都是索引列,所以即使使用了模糊查询也可以使用索引
-
如果
mysql
评估使用索引比全表扫描更慢则不会使用索引
先在
address
上创建索引
create index idx_item_address on tb_seller(address);
Address
上建了索引为什么这里没用索引呢?因为这张表中总共十条记录有九条都是北京市,所以用索引去查不如全表扫描快。
再来看一个查询
:
这里用到了索引。
10) in
走索引,
not in
不走索引