导读
执行计划中看到key_len值发生变化,表示联合索引有时能用到多个列,有时却只能用到部分列,这是为啥子呢?
我的朋友小明同学这几天又遇到一件令人懵*的事,有个SQL执行计划看起来挺诡异的,我看了下,也是有点发懵。
基本信息
严格遵循我自己要求的“提问的艺术”,先交代下必要的背景信息。
1、表DDL(建议手机横过来观看)
[root@yejr.me]>show create table t_keylen\G
*************************** 1. row ***************************
Table: t_keylen
Create Table: CREATE TABLE `t_keylen` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`userid` bigint(20) unsigned NOT NULL DEFAULT '0',
`balance` int(10) unsigned NOT NULL DEFAULT '0',
`type` tinyint(3) unsigned NOT NULL DEFAULT '0',
`fullName` json NOT NULL,
PRIMARY KEY (`id`),
KEY `i_userid_balance` (`userid`,`balance`)
) ENGINE=InnoDB AUTO_INCREMENT=19;
[root@yejr.me]>select * from t_keylen;
+----+--------+---------+------+-----------------------------------------------------------------+
| id | userid | balance | type | fullName |
+----+--------+---------+------+-----------------------------------------------------------------+
| 1 | 1 | 121 | 1 | {"lastName": "1", "firstName": "fn", "middleName": "mn"} |
| 2 | 2 | 1236 | 0 | {} |
| 3 | 3 | 100 | 0 | {"lastName": "3"} |
| 4 | 4 | 5 | 0 | {"lastName": "4", "firstName": "fn"} |
| 6 | 5 | 11 | 0 | {"lastName": "ln", "firstName": "fn", "middleName": "mn"} |
| 7 | 6 | 5 | 0 | {"lastName": "ln", "firstName": "fn", "middleName": "mn"} |
| 9 | 7 | 5 | 0 | {"lastName": "ln", "firstName": "fn", "middleName": "mn"} |
| 10 | 8 | 5 | 0 | {"lastName": "ln", "firstName": "fn", "middleName": "mn"} |
| 11 | 11 | 11 | 0 | {"lastName": "ln", "firstName": "fn", "middleName": "mn"} |
| 13 | 10 | 11 | 1 | {"lastName": "ln", "firstName": "fn", "middleName": "mn"} |
| 15 | 14 | 47 | 1 | {"lastName": "wqQSi", "firstName": "U1lc", "middleName": "ijK"} |
| 18 | 12 | 2 | 1 | {"lastName": "K196J", "firstName": "4Uf3", "middleName": "Vlt"} |
+----+--------+---------+------+-----------------------------------------------------------------+
12 rows in set (0.00 sec)
2、索引统计信息(有些输出不影响本案例判断,我给去掉了,建议手机横过来观看)
[root@yejr.me]>show index from t_keylen;
+------------+------------------+--------------+-------------+-------------+
| Non_unique | Key_name | Seq_in_index | Column_name | Cardinality |
+------------+------------------+--------------+-------------+-------------+
| 0 | PRIMARY | 1 | id | 12 |
| 1 | i_userid_balance | 1 | userid | 12 |
| 1 | i_userid_balance | 2 | balance | 12 |
+------------+------------------+--------------+-------------+-------------+
注意到 i_userid_balance 是一个联合索引,由两列构成,数据类型分别是 bigint 和 int,如果整个索引都被用上的话,其key_len值为12,如果只用到了userid列,则其key_len值为8。
3、查看SQL执行计划
先看第一条SQL的执行计划(建议手机横过来观看)
[root@yejr.me]>desc select * from t_keylen where
userid > 10 and balance > 1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t_keylen
partitions: NULL
type: range
possible_keys: i_userid_balance
key: i_userid_balance
key_len: 8
ref: NULL
rows: 3
filtered: 33.33
Extra: Using index condition
在本例中,key_len值为8,说明联合索引只用到了 userid 列,因为条件 userid > 10 是范围查询,看起来没毛病。
再看第二条SQL(建议手机横过来观看)
[root@yejr.me]>desc select * from t_keylen where
userid >= 10 and balance > 1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t_keylen
partitions: NULL
type: range
possible_keys: i_userid_balance
key: i_userid_balance
key_len: 12
ref: NULL
rows: 4
filtered: 33.33
Extra: Using index condition
这个结果就有点出乎意料了,不是说联合索引里,如果有一列开始是范围查询的话,那么从它后面的列都用不到索引了,但在本例中,我们看到的是 key_len=12,说明两个列还是都用上了,这是为啥呢?
4、解惑
请教了下知数堂《SQL优化》班的松华老师,终于解惑了。
原来对于数字型的联合索引,最左边的列的是否有等号非常重要,直接决定key_len的大小。本例中是 >= 和> 这种 左列包含等号 的条件,索引长度就可以计算到第二个列。如果是 > 和 > 这种条件,因为最左边的列不包含等号所以只能用到第一个。同理,下面案例是 <=和= 那就key_len值算到第二个列,即其值为12。(建议手机横过来观看)
[root@yejr.me]>desc select * from t_keylen where
userid <= 10 and balance = 1\G
*************************** 1. row ***************************
...
type: range
possible_keys: i_userid_balance
key: i_userid_balance
key_len: 12
...
好吧,又涨姿势了。