explain之key_len計算


通常在優化SQL查詢的時候,我們都會使用explain分析SQL執行計划,通常來說當用到組合索引的時候我們如何判斷索引完全用上呢?當然高手看看表結構及SQL語句就知道到底用到了幾個字段,對於不熟悉的同學呢?我們還是可以看看key_len的長度,當然這個計算還是有點復雜的,不過在你看過我這篇博客以后,相信你肯定會計算的,這難不倒聰明的你。

廢話不多說了,我們直接上例子。表結構如下。^_^

mysql [localhost] {msandbox} (yayun) > show create table t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` char(20) NOT NULL DEFAULT '',
  `name1` char(20) DEFAULT NULL,
  `name3` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql [localhost] {msandbox} (yayun) > 

上面的表結構非常簡單,有個主鍵索引,也就是id字段,還有一個輔助索引,也就是name字段,下面我們執行一條SQL,並分析一下執行計划,看看到底key_len如何計算的。
表中就3條記錄:

mysql [localhost] {msandbox} (yayun) > select * from t1;
+----+-------+-------+-----------+
| id | name  | name1 | name3     |
+----+-------+-------+-----------+
|  1 | atlas | yayun | dengyayun |
|  2 | alex  | talex | jalex     |
|  3 | je    | jetom | tomje     |
+----+-------+-------+-----------+
3 rows in set (0.00 sec)

mysql [localhost] {msandbox} (yayun) > 

下面進行explain進行查看key_len的長度(這里只講解key_len的計算,其他選項的意思請參看我前面的博客)

mysql [localhost] {msandbox} (yayun) > explain select * from t1 where name='atlas';
+----+-------------+-------+------+---------------+------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra                 |
+----+-------------+-------+------+---------------+------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | t1    | ref  | name          | name | 60      | const |    1 | Using index condition |
+----+-------------+-------+------+---------------+------+---------+-------+------+-----------------------+
1 row in set (0.03 sec)

mysql [localhost] {msandbox} (yayun) > 

可以看到key_len的長度是60,那么這個60是如何計算出來的。當然如果是單列索引我們不用去計算,因為沒有意義,如果是組合索引,那么知道這里的長度就是非常有意義的,我們先簡單來看看這個單列索引的key_len等於60是如何計算的。
還記得前面我的表結構里面name字段的定義么? 

`name` char(20) NOT NULL DEFAULT '',我定義了char(20),且非空。

好,現在我們來計算一下,首先我的表用的utf8字符集,那么大家都知道utf8字符集占用3個字節,那么我又定義char(20),知道結果了么?聰明的你一定知道了。

key_len=20*3=60

計算簡單吧,這個情況確實簡單,還有復雜的情況呢,嘿嘿。

我們下面繼續看下一條SQL,我們把name這個字段的索引去掉,添加一個聯合索引,key(name,name1)

mysql [localhost] {msandbox} (yayun) > alter table t1 drop key name;
Query OK, 0 rows affected (0.15 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql [localhost] {msandbox} (yayun) > alter table t1 add key idx_key_name_name1 (name,name1);
Query OK, 0 rows affected (0.29 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql [localhost] {msandbox} (yayun) > 

我們再來進行一條查詢:

mysql [localhost] {msandbox} (yayun) > explain select * from t1 where name='atlas';
+----+-------------+-------+------+--------------------+--------------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys      | key                | key_len | ref   | rows | Extra                 |
+----+-------------+-------+------+--------------------+--------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | t1    | ref  | idx_key_name_name1 | idx_key_name_name1 | 60      | const |    1 | Using index condition |
+----+-------------+-------+------+--------------------+--------------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)

mysql [localhost] {msandbox} (yayun) > explain select * from t1 where name='atlas' and name1='yayun';
+----+-------------+-------+------+--------------------+--------------------+---------+-------------+------+-----------------------+
| id | select_type | table | type | possible_keys      | key                | key_len | ref         | rows | Extra                 |
+----+-------------+-------+------+--------------------+--------------------+---------+-------------+------+-----------------------+
|  1 | SIMPLE      | t1    | ref  | idx_key_name_name1 | idx_key_name_name1 | 121     | const,const |    1 | Using index condition |
+----+-------------+-------+------+--------------------+--------------------+---------+-------------+------+-----------------------+
1 row in set (0.04 sec)

mysql [localhost] {msandbox} (yayun) > 

看到第一條查詢和第二條的查詢的執行計划有什么不同了么?沒錯,key_len及ref列不一樣了。why?以及為什么第二條SQL語句的key_len為121,這個是如何計算的?嘿嘿,如果還用上面的計算方法你肯定計算不出來的。讓我來告訴你。還記得name1字段的定義么?
 `name1` char(20) DEFAULT NULL,

可以發現name1字段的定義為DEFAULT NULL,其他沒變化。所以MySQL需要1個字節來標識NULL,

所以第二條SQL的key_len=20 * 3 + (20 * 3 +1)=121,通過計算,我們知道2個字段的索引完全用上了。

下面我們再繼續看看其他的情況,給表添加一個字段,並添加一個聯合索引,我們進行一個范圍的查詢。

mysql [localhost] {msandbox} (yayun) > alter table t1 add add_time timestamp;
Query OK, 0 rows affected (1.44 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql [localhost] {msandbox} (yayun) > alter table t1 add key idx_key_add_time_name3 (add_time,name3);        
Query OK, 0 rows affected (0.19 sec)
Records: 0  Duplicates: 0  Warnings: 0

現在的表結構這樣了。

mysql [localhost] {msandbox} (yayun) > show create table t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` char(20) NOT NULL DEFAULT '',
  `name1` char(20) DEFAULT NULL,
  `name3` varchar(20) NOT NULL DEFAULT '',
  `add_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_key_name_name1` (`name`,`name1`),
  KEY `idx_key_add_time_name3` (`add_time`,`name3`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
1 row in set (0.01 sec)

mysql [localhost] {msandbox} (yayun) > 

看SQL,廢話不多說。

mysql [localhost] {msandbox} (yayun) > explain select * from t1 where add_time >='2014-09-10 02:36:46' and add_time <='2014-09-11 02:36:46' group by name3 order by null;
+----+-------------+-------+-------+------------------------+------------------------+---------+------+------+----------------------------------------+
| id | select_type | table | type  | possible_keys          | key                    | key_len | ref  | rows | Extra                                  |
+----+-------------+-------+-------+------------------------+------------------------+---------+------+------+----------------------------------------+
|  1 | SIMPLE      | t1    | range | idx_key_add_time_name3 | idx_key_add_time_name3 | 4       | NULL |    2 | Using index condition; Using temporary |
+----+-------------+-------+-------+------------------------+------------------------+---------+------+------+----------------------------------------+
1 row in set (0.00 sec)

mysql [localhost] {msandbox} (yayun) > 

可以看見用到了我創建的聯合索引idx_key_add_time_name3,但是真的完全用到了么。其實一眼就知道沒有用到,因為前面是一個范圍查詢,后面字段的索引就用不到,如果我這里不order by null,還會看到Using filesort。但是我還是想說說key_len是如何計算的,大家都很清楚timestamp占用4字節吧。那么答案顯而易見,看見key_len是4,說明只用到了聯合索引idx_key_add_time_name3中的add_time字段。

我們再來看一種情況,是char字段和varchar字段組成的一個聯合索引。

mysql [localhost] {msandbox} (yayun) > alter table t1 add key idx_key_name1_name3 (name1,name3);
Query OK, 0 rows affected (0.27 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql [localhost] {msandbox} (yayun) > 

SQL如下:

mysql [localhost] {msandbox} (yayun) > explain select * from t1 where name1='yayun' and name3='dengyayun';
+----+-------------+-------+------+---------------------+---------------------+---------+-------------+------+-----------------------+
| id | select_type | table | type | possible_keys       | key                 | key_len | ref         | rows | Extra                 |
+----+-------------+-------+------+---------------------+---------------------+---------+-------------+------+-----------------------+
|  1 | SIMPLE      | t1    | ref  | idx_key_name1_name3 | idx_key_name1_name3 | 123     | const,const |    1 | Using index condition |
+----+-------------+-------+------+---------------------+---------------------+---------+-------------+------+-----------------------+
1 row in set (0.00 sec)

mysql [localhost] {msandbox} (yayun) > 

可以看見key_len的長度是123。那么索引完全用到了么?當然有點索引常識都知道完全用到了。我這里只是為了告訴大家key_len到底如何計算的。
`name3` varchar(20) NOT NULL DEFAULT ''

`name1` char(20) DEFAULT NULL,

上面是2個字段的定義,1個允許NULL,一個NOT NULL,一個char,一個varchar

所以key_len=(20*3 + 1)+(20 * 3 + 2)= 123

由此來判斷這個組合索引已經完全使用。相信有同學會問了,+1是干嘛,+2是干嘛。這就告訴大家,+1是因為MySQL需要1個字節標識NULL,+2是因為name3字段為varchar,是變長字段需要+2。

寫到這里相信大家都有一個基本認識了吧。好了,多的不說了,公式放出來給大家,自己套用公式,多做幾次測試就明白鳥。

 

key_len的長度計算公式:
varchr(10)變長字段且允許NULL    =  10 * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)+2(變長字段)
varchr(10)變長字段且不允許NULL =  10 *( character set:utf8=3,gbk=2,latin1=1)+2(變長字段)

char(10)固定字段且允許NULL        =  10 * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)
char(10)固定字段且不允許NULL        =  10 * ( character set:utf8=3,gbk=2,latin1=1)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM