mysql索引和慢查询

1.工作中,可能我们会遇到有些sql语句,我们用了索引,为什么还会记录在慢查询日志中呢?

慢查询:是把sql的执行时间跟long_query_time这个系统参数作比较,大于这个时间,就写写入慢查询日志,通常是1s;查询慢查询的sql语句为:

show variables like 'long_query_time';

设置慢查询的sql语句为:

set long_query_time=1

通过一张表,我们来看看索引:表的ddi语句

create table testTable(
 id int(11) not null,
  a int(11) DEFAULT NULL,
  b int(11) DEFAULT NULL,
PRIMARY key (id),
KEY a (a)

) ENGINE=INNODB

而对于索引,是指sql语句是否执行了索引,比如下面的几个例子:

sql语句为:select * from testTable;key栏是null,是没有索引的,如下图:

sql语句为:select * from t where id=2;key 是Primary,使用了主键索引

 sql语句:select a from testtable;key是啊,说明使用到了a索引

图2和图3都使用了索引,但是图3 是扫描了整个索引书,图2只扫描了一行,如果数据很多的话,图3执行的时间就比较慢了,如果极端的情况下,可能图2的执行也会超过设置的慢查询时间,是否使用索引只是说一个sql的执行过程,而是否进入慢查询是有sql执行的时间决定的,取决于好多元素,所以使用了索引和慢查询没有必然的联系。

执行select * from testtable where id>0;看执行情况是主键索引,看扫描行数一定是全表扫描,但是解释器认为需要查询到第一条id>0的数据,所以使用了索引。

还有那些除了全表扫描,那些只用了索引,执行不够快的例子呢?这就说到了索引的过滤性

假设有一张表,记录了一个城市的人员信息,表的语句

CREATE TABLE `user` (
  `userid` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) NOT NULL,
  `password` varchar(20) NOT NULL,
  `sex` varchar(4) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`userid`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

 如果我们相差年龄在8到12的小朋友,我们的sql语句可能会这样写,

select * from user where age BETWEEN 8 and 12;

这个一般我们会在age上建索引,建了索引之后,执行的时间还是很长,因为数据很大。扫描的行数很大,所以我们在讨论有没有使用索引的时候,我们关心的是扫描行数,对于一个大表,不仅要有索引,索引的过滤性也要比较好。age的索引过滤性不好,索引的区分度要比较高,想身份证号。如果你的过滤性好了,是不是扫描的行数就变少呢,如果这个表上有一个年龄+姓名的过滤性应该不错。如果sql语句是

select * from user where username='张三' AND AGE=3

就可以在索引上快速找到符合条件的数。但是我们如果想查询姓张的并且年龄为8的小朋友,我们的sql语句估计会这样写:

select * from user where username Like '张%' AND age=8

但是这个sql执行过程中回表的次数比较多。mysql 5.6引入了 index condition pushdown的优化。和上面的相比,讲age=3这个条件下推到遍历索引树上,减少了回表的次数。但是这个优化还是没有绕开最左前缀的限制

我们还可以把姓名的第一个字和年龄做一个联合索引,这里可以使用mysql引入的虚拟列来实现。

sql语句是这样写的:

ALTER TABLE `user` add name_first VARCHAR(2) generated always as (LEFT(username,1)),add INDEX(name_first,age);

执行完成会后,查看表的ddl语句:

CREATE TABLE `user` (
  `userid` int NOT NULL AUTO_INCREMENT,
  `username` varchar(20) NOT NULL,
  `password` varchar(20) NOT NULL,
  `sex` varchar(4) DEFAULT NULL,
  `age` int DEFAULT NULL,
  `name_first` varchar(2) GENERATED ALWAYS AS (left(`username`,1)) VIRTUAL,
  PRIMARY KEY (`userid`),
  KEY `name_first` (`name_first`,`age`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

这个name_first的值不能修改,有定义自动生成,我们如果需要查询姓张且年龄等于22的小朋友,可以这样写

SELECT * FROM `user` where name_first='张' AND age=22;

这个减少了扫描的行数和回表的次数。这次优化的本质就是创建了一个更紧凑的索引。来加速查询的过程。

最后,使用了索引的过程也有可能慢查询,优化的过程就是减少扫描的行数。和回表的次数。 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值