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。
写到这里相信大家都有一个基本认识了吧。好了,多的不说了,公式放出来给大家,自己套用公式,多做几次测试就明白鸟。
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)
explain之key_len计算的更多相关文章
- mysql explain 中key_len的计算
		
今天丁原问我mysql执行计划中的key_len是怎么计算得到的,当时还没有注意,在高性能的那本书讲到过这个值的计算,但是自己看执行计划的时候一直都没有太在意这个值,更不用说深讨这个值的计算了: ke ...
 - Explain之key_len长度计算
		
以前我很好奇通过执行计划Explain去分析SQL的时候看到的key_len值有时很小,有时看到很大,那时也不知道它是怎么算出来的,现在终于搞懂了,嘻.因为网上对key_len的长度的计算的资料也很少 ...
 - mysql explain中key_len的作用
		
mysql explain中key_len的作用key_len越小 索引效果越好 name的字段类型是varchar(20),字符编码是utf8,一个字符占用3个字节,那么key_len应该是 20* ...
 - mysql explain中key_len的计算
		
ken_len表示索引使用的字节数,根据这个值,就可以判断索引使用情况,特别是在组合索引的时候,判断是否所有的索引字段都被查询用到. key_len显示了条件检索子句需要的索引长度,但 ORDER B ...
 - Mysql explain中key_len的作用及计算规则
		
key_len表示索引使用的字节数,根据这个值可以判断索引的使用情况,特别是在组合索引的时候,判断该索引有多少部分被使用到非常重要. 在计算key_len时,下面是一些需要考虑的点: 索引字段的附加信 ...
 - MySQL执行计划explain的key_len解析
		
前言:当用Explain查看SQL的执行计划时,里面有列显示了 key_len 的值,根据这个值可以判断索引的长度,在组合索引里面可以更清楚的了解到了哪部分字段使用到了索引.下面演示中,表结构的合理性 ...
 - mysql explain中key_len值的说明
		
在mysql 的explain的输出中,有个key_len的列,其数据是如何计算的呢? 在看到了淘宝的dba以前发布的博客后,我在mysql 5.6上操作一番,了解了一点. 环境准备 – 创建表. u ...
 - MySQL的查询计划中ken_len的值计算
		
本文首先介绍了MySQL的查询计划中ken_len的含义:然后介绍了key_len的计算方法:最后通过一个伪造的例子,来说明如何通过key_len来查看联合索引有多少列被使用. key_len的含义 ...
 - 【MySQL】优化—工欲善其事,必先利其器之EXPLAIN
		
接触MySQL已经有一段时间了,了解如何优化它也迫在眉睫了,话说工欲善其事,必先利其器.最近我就打算了解下几个优化MySQL中经常用到的工具.今天就简单介绍下EXPLAIN. 环境准备 Explain ...
 
随机推荐
- ABBYY OCR技术教电脑阅读缅甸语(下)
			
文本行检测到之后,我们开始寻找单词和字母之间的间隙,这一次,我们运用了水平直方图,将大的间隙假设为单词之间的空隙,小的间隙理解为字母之间的空隙,检测缅甸文本中的空隙几乎没有出现问题,不像泰语,几乎没有 ...
 - IOC容器特性注入第二篇:初始引擎查找对应的IOC容器
			
上篇文章介绍了如何利用反射类查找网站bin文件夹下面所有DLL的程序集类,这篇文章将介绍如何初始化一个IOC容器引擎. 目前IOC容器有很多,如Ninject,Autofac等,每个容器的驱动都不一样 ...
 - ios开发之--关于UIView的autoresizingMask属性的研究
			
在 UIView 中有一个autoresizingMask的属性,它对应的是一个枚举的值(如下),属性的意思就是自动调整子控件与父控件中间的位置,宽高. enum { UIViewAutoresizi ...
 - Ubuntu14.04下安装redis-3.2.0以及开机自启动
			
去官网下载Redis-3.2.0.tar.gz,将redis-3.2.0.tar.gz放入/opt目录下 解压redis-3.2.0.tar.gz xiaoyao@xiaoyao-virtual-ma ...
 - Linux Platform驱动模型(三) _platform+cdev
			
平台总线是一种实现设备信息与驱动方法相分离的方法,利用这种方法,我们可以写出一个更像样一点的字符设备驱动,即使用cdev作为接口,平台总线作为分离方式: xjkeydrv_init():模块加载函数 ...
 - Android Studio开发第二篇创建新项目
			
创建新项目很简单,File-New-New Project,这个没什么好说的跟Eclipse都差不都. 第二步SDK选择,有手机平板还有Wear,TV,汽车Auto,谷歌眼镜等几个种平台,这里就先选择 ...
 - 【laravel5.6】 laravel 执行 php artisan route:cache 报错 Unable to prepare route [/] for serialization. Uses Closure.
			
laravel 在部署的时候.需要优化路由加载,执行命令 php artisan route:cache 报错了.如下 这个异常的错误信息,提示的已经非常明确了:大概意思就是说在闭包里边,是不能够进行 ...
 - 电子书阅读(epub) ---  calibre
			
https://calibre-ebook.com/download calibre https://www.fosshub.com/Calibre.html win 64 bit calib ...
 - get_or_create函数
			
get_or_create函数比较好用. 如果查询到就返回,如果没查询到就向数据库加入新的对象. e.g. size = Size.objects.get_or_create(sizeName=siz ...
 - F - Communication System
			
We have received an order from Pizoor Communications Inc. for a special communication system. The sy ...