相似的SQL执行计划key_len为什么不同

640?wx_fmt=png

导读

执行计划中看到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
...

好吧,又涨姿势了。

相关阅读


END




640?wx_fmt=png

640?wx_fmt=png



640?wx_fmt=gif


扫码加入MySQL的技术Q群

(群号:579036588)

   

640?wx_fmt=jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值