慢查询为查询时间超过指定值,mysql中语句执行超过设定值long_query_time,即被记录到慢查询日志中去,一般作为查询的优化手段,建立索引是一个比较不错的选择,那么我们可以说我们建立好索引后,sql用到了此索引,就可以解决慢查询问题了吗?
其实,mysql慢查询与索引其实没有必然的联系,因为查询的过滤性与索引的过滤性不同。我们以下面为例子。
现在我们有一个用户列表,表结构如下:
名称 | 类型 | 长度 |
id | bigint | 0 |
name | varchar | 10 |
phone | varchar | 20 |
age | int | 0 |
adreess | varchar | 50 |
字段name与age建立联合索引index_name|_age(name,age);
假设我们现在要查询表中符合name为张三,年龄为20的数据,我们的sql为:
select t.* from t_test t where t.name ='张三' and t.age=20
这时候我们发现查询的语句很快,原因如下:
1.符合条件的语句很少;
2.索引的过滤性足够好,根据最左匹配原则,name与age是查询全等数据,且Btree的查询规则为顺序查询,那么在维护的联合索引树上只要从用第一个符合name为张三,age为20,顺序查找到name不为张三,或者age不为20的数据即可,
以我的模拟数据为例子,表中的模拟数据如下:
id | name | phone | age | adreess |
1 | 张三 | 13100000000 | 20 | 北京市 |
2 | 张三 | 13100000000 | 20 | 天津市 |
3 | 张三 | 13100000000 | 21 | 武汉市 |
4 | 张四 | 13100000000 | 20 | 石家庄市 |
下面我们看下sql执行计划,语句为:
explain select t.* from t_test t where t.name ='张三' and t.age=20
执行结果如下:
d | select_type | table | partitions | type | possible_keys | key | key_length | ref | rows | filtered | Extra |
1 | SIMPLE | t | | ref | iindex_name_age | index_name_age | 38 | const | 2 | 100 | |
根据rows可以看出,我们只查询了符合条件的2行数据。
那么下面我们有了一个新的要求,要查询出符合name第一个名字为张的,年龄为20的数据,那么我们该怎么办呢,我们可能会这样编写sql:
select t.* from t_test t where t.name like '张%' and t.age=20
查询结果是我们想要的,并且查看下执行计划:
d | select_type | table | partitions | type | possible_keys | key | key_length | ref | rows | filtered | Extra |
1 | SIMPLE | t | | range | iindex_name_age | index_name_age | 38 | | 4 | 100 | Using index condition |
我们用到了我们建立的联合索引,这时我们可能以为达到要求了,但是rows中的4,数据扫描了表中的所有数据,即为四行,同时,Extra中Using index condition 为通过联合索引优化了你的回表次数,那么我们可以减少表的扫描行数吗,答案是肯定的。我们使用5.7版本后引入的虚拟列,具体语法如下:
ALTER TABLE t_test ADD name_first VARCHAR (2) generated always AS (LEFT(NAME, 1)),ADD INDEX (name_first, age);
这时我们看表中就多了一个名为name_first的列,并且多了一个名字为name_first的索引,此时的name_first列不可更改与指定,是随着我们name字段自动生成的,这时我们重新编写sql语句:
select t.* from t_test t where t.name_first ='张' and age =20
我们看下执行计划,语句省略,执行计划如下:
d | select_type | table | partitions | type | possible_keys | key | key_length | ref | rows | filtered | Extra |
1 | SIMPLE | t | | ref | name_first | name_first | 14 | const,const | 3 | 100 | |
可以看到,根据最左匹配原则,过滤器只需要查找符合规定的数据即可,同样省去了优化器帮助我们减少回表次数。
这时由于之前的索引树中,数据存储的顺序是这样的(树形状省略):(张三,20),(张三,20),(张三,21),(张四,20),这样一来顺序匹配的要经过(张三,21),所以为四行。
我们加入虚拟列之后的联合索引存储数据为(树形状省略):(张,20),(张,20),(张,20),(张,21),这样我们只要根据顺序匹配前三个即可,然后根据取出的主键id,进行回表数据查找。