MySql基础笔记(二)Mysql语句优化---索引
Mysql语句优化--索引
一、开始优化前的准备
一)explain语句
当MySql要执行一个查询语句的时候,它首先会对语句进行语法检查,然后生成一个QEP(Query Execution Plan)
QEP决定了MySql从底层存储引擎获取信息的方式。
语法:explain [extended | partitions] select ......
或者:explain tableName (此时相当与desc语句)
explain select * from artist where name='The Beatles'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: artist
partitions: NULL
type: ref
possible_keys: name
key: name
key_len: 257
ref: const
rows: 1
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec) ERROR:
No query specified
各列详解:
1.key
列出了优化器本次实际使用的索引。
2.possible_keys
优化器可能使用的索引。
3.rows
行数估算值
4.key_len
定义了用于SQL语句的链接条件的键的长度(表明了索引的大小)。
key_len的值只和用在链接和where条件中的索引的列有关。
5.partistions
代表给定表所使用的分区。
二)其他
1.show table status:
用于查看数据库表的底层大小以及表结构,其中包括存储引擎类型、版本、数据和索引大小、行的平均长度及行数。
show table status like 'artist'\G;
这条命令返回的数据准确度取决于使用的数据库引擎。
2.show status:
用于查看MySql服务器的当前内部状态信息。
show global status like 'Created_tmp_%tables';
show session status like 'Created_tmp_%tables';
3.show variables
查看mysql系统变量的当前值,默认在session范围内执行。
show session variables like 'tmp_table_size';
4.show index
查看索引信息
show index from artist\G;
二、单列索引
对select语句where,order by,group by 后的条件所在列添加索引可以提高查询速度。
一)理解索引基数
当一个查询中使用不止一个索引时,Mysql会试图找到一个最高效的索引。
使用到的表:
| artist | CREATE TABLE `artist` (
`artist_id` int(10) unsigned NOT NULL,
`type` enum('Band','Person','Unknown','Combination') NOT NULL,
`name` varchar(255) NOT NULL,
`gender` enum('Male','Female') DEFAULT NULL,
`founded` year(4) DEFAULT NULL,
`country_id` smallint(5) unsigned DEFAULT NULL,
PRIMARY KEY (`artist_id`),
KEY `founded` (`founded`),
KEY `type` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
explain语句:
explain select * from artist where type='Band' and founded=1980\G;
结果:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: artist
partitions: NULL
type: index_merge
possible_keys: founded,type
key: founded,type
key_len: 2,1
ref: NULL
rows: 608
filtered: 100.00
Extra: Using intersect(founded,type); Using where
1 row in set, 1 warning (0.05 sec) ERROR:
No query specified
此时,mysql必须在possible_keys中做出选择,优化器会根据最少工作量的估算开销来选择索引。
我们可以使用索引基数来确定最有可能被选中的索引。
mysql> show index from artist\G;
*************************** 1. row ***************************
Table: artist
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1
Column_name: artist_id
Collation: A
Cardinality: 581100
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
*************************** 2. row ***************************
Table: artist
Non_unique: 1
Key_name: founded
Seq_in_index: 1
Column_name: founded
Collation: A
Cardinality: 104
Sub_part: NULL
Packed: NULL
Null: YES
Index_type: BTREE
Comment:
Index_comment:
*************************** 3. row ***************************
Table: artist
Non_unique: 1
Key_name: type
Seq_in_index: 1
Column_name: type
Collation: A
Cardinality: 3
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
3 rows in set (0.00 sec)
ERROR:
No query specified
其中,Cardinality就是所谓的基数。其中,founded列拥有更高的基数。如果列索引的基数越高,
就说明列中唯一的数量越多,也就可以在选用该索引时,通过更少的读操作找到需要的记录。
当然仅仅知道索引中唯一值的数量意义不大,重要的是要知道该值与总数量的比值。理想情况下该比值为1。
这些信息不但可以帮助我们判断索引是否高效,还可以告诉我们如何在多列索引中对列进行排序。
二)索引与通配符
我们可以通过通配符来做模式匹配工作,但是:
1. 如果查找的词是以通配符开头,则MySql不会使用索引。
mysql> explain select * from artist where name like '%the beatles'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: artist
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 581100
filtered: 11.11
Extra: Using where
解决方案:
如果你经常需要以通配符开头的查询,常用的方法是在数据库中保存需要查询值的反序值。例如:
想查找以.com结尾的电子邮件地址,首先定义一个reserve_email列存储电子邮件的反序值。查询时
使用:reserve_email like reserve(%.com)
2.MySql不支持基于索引的函数。
mysql> explain select * from artist where upper(name) = 'QUEEN'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: artist
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 581100
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
因为在name列上使用了upper函数,所以mysql不会使用索引。
三)唯一索引
创建唯一索引的目的:
1)提供数据完整性,以保证列中的任何值只出现一次
2)告知优化器对给定的记录最多只有一行返回结果,避免额外的扫描
四)结果排序
索引也可对查询结果进行排序。基于索引的排序,免去了分类过程,更优。
技巧:也就是说给order by 后面的条件列,可以加索引优化。
五)使用索引链接表
索引的另一个好处是可以提高关系表的链接速度
mysql> explain select ar.name, ar.founded, al.name, al.first_released from artist ar inner join album al using (artist_id) where ar.name = 'Queen'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: ar
partitions: NULL
type: ref
possible_keys: PRIMARY,name_index
key: name_index
key_len: 257
ref: const
rows: 1
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: al
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 552034
filtered: 10.00
Extra: Using where; Using join buffer (Block Nested Loop)
2 rows in set, 1 warning (0.12 sec)
为链接条件添加索引
mysql> alter table album add index (artist_id);
Query OK, 0 rows affected (29.24 sec)
Records: 0 Duplicates: 0 Warnings: 0 mysql> explain select ar.name, ar.founded, al.name, al.first_released from artist ar inner join album al using (artist_id) where ar.name = 'Queen'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: ar
partitions: NULL
type: ref
possible_keys: PRIMARY,name_index
key: name_index
key_len: 257
ref: const
rows: 1
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: al
partitions: NULL
type: ref
possible_keys: artist_id
key: artist_id
key_len: 4
ref: music.ar.artist_id
rows: 3
filtered: 100.00
Extra: NULL
2 rows in set, 1 warning (0.03 sec)
二、多列索引
根据国家和专辑类型查询专辑表中的数据,如果我们不使用多列索引,先创建单列索引:
mysql> alter table album
-> add index (country_id),
-> add index (album_type_id);
技巧:创建索引是一件非常耗时的操作,并且会阻塞其他操作。尽可能合并DML语句会获得更高的效率。
mysql> explain select * from album where country_id = 221
-> and album_type_id = 1\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: album
partitions: NULL
type: ref
possible_keys: country_id,album_type_id
key: country_id
key_len: 3
ref: const
rows: 169590
filtered: 50.00
Extra: Using where
1 row in set, 1 warning (0.04 sec)
对album type使用不同值运行同一个查询,那么会使用不同的索引。
使用多列索引,创建多列索引:
mysql> alter table album
-> add index m1
-> (country_id, album_type_id);
重新运行语句:
mysql> explain select * from album where
-> country_id = 221
-> and album_type_id = 1\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: album
partitions: NULL
type: ref
possible_keys: country_id,album_type_id,m1
key: m1
key_len: 7
ref: const,const
rows: 61784
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.06 sec)
创建相反序列的索引:
mysql> alter table album
-> add index m2
-> (album_type_id, country_id);
再次查询,mysql还是使用m1索引。
查找原因:
show index from album\G;
*************************** 1. row ***************************
Table: album
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1
Column_name: album_id
Collation: A
Cardinality: 552034
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
*************************** 2. row ***************************
Table: album
Non_unique: 1
Key_name: artist_id
Seq_in_index: 1
Column_name: artist_id
Collation: A
Cardinality: 156672
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
*************************** 3. row ***************************
Table: album
Non_unique: 1
Key_name: country_id
Seq_in_index: 1
Column_name: country_id
Collation: A
Cardinality: 202
Sub_part: NULL
Packed: NULL
Null: YES
Index_type: BTREE
Comment:
Index_comment:
*************************** 4. row ***************************
Table: album
Non_unique: 1
Key_name: album_type_id
Seq_in_index: 1
Column_name: album_type_id
Collation: A
Cardinality: 3
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
*************************** 5. row ***************************
Table: album
Non_unique: 1
Key_name: m1
Seq_in_index: 1
Column_name: country_id
Collation: A
Cardinality: 190
Sub_part: NULL
Packed: NULL
Null: YES
Index_type: BTREE
Comment:
Index_comment:
*************************** 6. row ***************************
Table: album
Non_unique: 1
Key_name: m1
Seq_in_index: 2
Column_name: album_type_id
Collation: A
Cardinality: 702
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
*************************** 7. row ***************************
Table: album
Non_unique: 1
Key_name: m2
Seq_in_index: 1
Column_name: album_type_id
Collation: A
Cardinality: 4
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
*************************** 8. row ***************************
Table: album
Non_unique: 1
Key_name: m2
Seq_in_index: 2
Column_name: country_id
Collation: A
Cardinality: 463
Sub_part: NULL
Packed: NULL
Null: YES
Index_type: BTREE
Comment:
Index_comment:
技巧:创建多列索引时,交换顺序可能创建出更好的索引。
多列索引除了优化返回的行之外还有更多用途,多列索引最左边的列可以被当作单列索引高效地使用。
当这些列被频繁地用于聚合操作(group by)和排序操作(order by)时,最左边的列也可提升性能。
另外,同样的查询条件下,索引的行应该尽可能短,这样可以提高效率。所以,多余的索引要去掉。
优化器对索引的选择
一般情况下,优化器在一个表上的一次查询只会使用一个索引。但从Mysql5.0版本开始,
优化器可能会使用多个索引。
1)对两个有很高基数的所有使用or操作时,优化器会执行索引的合并操作
mysql> explain select * from artist
-> where name='Queen' or founded=1942\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: artist
partitions: NULL
type: index_merge
possible_keys: founded,name_index
key: name_index,founded
key_len: 257,2
ref: NULL
rows: 500
filtered: 100.00
Extra: Using union(name_index,founded); Using where
2)对两个有少量唯一值的索引取交集。
3)
技巧:应该评估使用多列索引,还是让优化器合并索引哪个的效率更高。
查询提示
Mysql中少数几个查询提示会影响性能。
1.总查询提示
总查询提示中只有STRAIGHT_JOIN查询提示会对索引的使用有影响。该提示会让优化器
以指定索引顺序来执行查询计划。例如:
没有STRAIGHT_JOIN提示时:
mysql> explain select al.name, ar.name, al.first_released from artist ar inner join album al using (artist_id) where al.name='Greatest Hits'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: al
partitions: NULL
type: ref
possible_keys: artist_id,name,name_realease
key: name
key_len: 257
ref: const
rows: 660
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: ar
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: music.al.artist_id
rows: 1
filtered: 100.00
Extra: NULL
加入STRAIGHT_JOIN提示:
mysql> explain select STRAIGHT_JOIN al.name, ar.name, al.first_released
-> from artist ar inner join album al using (artist_id)
-> where al.name='Greatest Hits'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: ar
partitions: NULL
type: index
possible_keys: PRIMARY
key: name_index
key_len: 257
ref: NULL
rows: 581100
filtered: 100.00
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: al
partitions: NULL
type: ref
possible_keys: artist_id,name,name_realease
key: artist_id
key_len: 4
ref: music.ar.artist_id
rows: 3
filtered: 1.42
Extra: Using where
2.索引提示
索引提示有三种:
USE:使用指定的索引
IGNORE:排除指定的索引
FORCE:让优化器更倾向与索引扫描而不是全表扫描
mysql> explain select * from artist where founded=1980
-> and type='Band'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: artist
partitions: NULL
type: ref
possible_keys: founded,a1
key: founded
key_len: 2
ref: const
rows: 1217
filtered: 50.00
Extra: Using index condition; Using where
1 row in set, 1 warning (0.03 sec) ERROR:
No query specified mysql> explain select * from artist
-> ignore index (founded)
-> where founded=1980
-> and type='Band'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: artist
partitions: NULL
type: ref
possible_keys: a1
key: a1
key_len: 1
ref: const
rows: 290550
filtered: 0.96
Extra: Using where
技巧:数据量随着时间的变化会改变某些查询的有效性,因此在硬编码的sql语句中加入查询提示是最后考虑的方案。
索引的负面影响
1.影响写操作的性能
2.占用额外的磁盘空间,从而影响备份和恢复策略
3.低效索引占用很大的内存空间
覆盖索引
一个覆盖索引必须包含指定表上包括where,order by,group by以及select语句中的所有列。
使用覆盖索引可以进一步提升查询性能。
技巧:有多种理由可以说服用户不要使用select *。覆盖索引就是原因之一。
另外,由于覆盖索引包含的列很可能很多,因此创建覆盖索引很可能会影响整体
性能。覆盖索引对于那些使用很多较小长度的主码和外键约束的大型规范化模式
来说是理想的优化方式。
局部索引
把一个索引剪切出开头一部分。例如:
mysql> alter table artist
-> drop index name_index,
-> add index name_part(name(20)); mysql> explain select * from artist
-> where name like 'Queen%'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: artist
partitions: NULL
type: range
possible_keys: name_part
key: name_part
key_len: 22
ref: NULL
rows: 93
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.03 sec)
MySql基础笔记(二)Mysql语句优化---索引的更多相关文章
- Mysql基础(二):MySQL之存储引擎
目录 MySQL之存储引擎 1.MySQL存储引擎介绍 2.MySQL结构 3.MySQL存储引擎分类 4.存储引擎的使用 5.总结 MySQL之存储引擎 1.MySQL存储引擎介绍 MySQL中的数 ...
- MySQL基础(二)——DDL语句
MySQL基础(二)--DDL语句 1.什么是DDL语句,以及DDL语句的作用 DDL语句时操作数据库对象的语句,这些操作包括create.drop.alter(创建.删除.修改)数据库对象. 2.基 ...
- MYSQL基础笔记(二)-SQL基本操作
SQL基本操作 基本操作:CRUD,增删改查 将SQL的基本操作根据操作对象进行分类: 1.库操作 2.表操作 3.数据操作 库操作: 对数据库的增删改查 新增数据库: 基本语法: Create da ...
- MYSQL基础笔记(三)-表操作基础
数据表的操作 表与字段是密不可分的. 新增数据表 Create table [if not exists] 表名( 字段名 数据类型, 字段名 数据类型, 字段n 数据类型 --最后一行不需要加逗号 ...
- MYSQL基础笔记(一)
关系型数据库概念: 1.什么是关系型数据库? 关系型数据库:是一种建立在关系模型(数学模型)上的数据库 关系模型:一种所谓建立在关系上的模型. 关系模型包含三个方面: 1.数据结构:数据存储的问题,二 ...
- MYSQL基础笔记(六)- 数据类型一
数据类型(列类型) 所谓数据烈性,就是对数据进行统一的分类.从系统角度出发时为了能够使用统一的方式进行管理,更好的利用有限的空间. SQL中讲数据类型分成三大类:1.数值类型,2.字符串类型和时间日期 ...
- MYSQL基础笔记(四)-数据基本操作
数据操作 新增数据:两种方案. 1.方案一,给全表字段插入数据,不需要指定字段列表,要求数据的值出现的顺序必须与表中设计的字段出现的顺序一致.凡是非数值数据,到需要使用引号(建议使用单引号)包裹. i ...
- MYSQL基础笔记(五)- 练习作业:站点统计练习
作业:站点统计 1.将用户的访问信息记录到文件中,独占一行,记录IP地址 <?php //站点统计 header('Content-type:text/html;charset=utf-8'); ...
- MySQL基础配置之mysql的默认字符编码的设置(my.ini设置字符编码) - 转载
MySQL基础配置之mysql的默认字符编码的设置(my.ini设置字符编码) MySQL的默认编码是Latin1,不支持中文,那么如何修改MySQL的默认编码呢,下面以设置UTF-8为例来说明. 需 ...
随机推荐
- php安装扩展
php安装扩展 以前以为php的扩展要重新编译php,今天在群友的指点下知道可以像apache模块一样动态扩展,以mcrypt举例. 进入要安装的扩展的源码目录cd /root/php-5.2.6/e ...
- Ajax爬虫必用到的字典转换器
1.使用情景 在我们Ajax爬虫时需要用到以下这样的数据的时候我们会一个一个地复制粘贴,这样会很麻烦 def dictionary_converter(key_value): '''主要用于爬虫时复制 ...
- zabbix 3.2.4 使用详解
一:zabbix简介及原理 二:zabbix添加主机: /usr/share/zabbix/include/locales.inc.php #这里为zabbix语言包路径‘zh_CN’ 为true ...
- Quartz.NET(任务调度)与Topshelf(服务)的综合使用
http://www.cnblogs.com/jys509/p/4628926.html http://cron.qqe2.com/ Quartz_Topshlf_Demo.7z
- sqlserver数据库不能重命名报错5030
在学习asp.net的时候使用mssql'经常会出现这种错误,数据库不能重名名5030的错误,其实很简单原因就是有应用程序正在占用这个连接,使用这样一行命令就可以查询出正在占用的连接 use mast ...
- 删除TFS中的项目
打开“Team Foundation Server 管理控制台”,选择应用层->团队项目集合->团队项目->点击项目集合名称,最后点击右侧出现的删除按钮
- jxoi2017
题解: 并不知道题目顺序就按照难度排序了 [JXOI2017]加法 这是一道很简单的贪心 最小值最大二分答案 然后我们可以从左向右考虑每一个位置 如果他还需要+A 我们就从能覆盖它的区间中挑一个最右的 ...
- Get与Post区别小结
Get:是以实体的方式得到由请求Url所指定资源的信息,如果请求Url只是一个数据产生过程,那么最终要在实体中返回的是处理过程的结果所指向的资源,而不是处理过程的描述. Post:是用来向 ...
- windows搭建RocketMQ服务
写在前面: 1.基于rocketmq 4.2.0版本 2.64位 win10 系统 3.JDK 1.8 (注意,jdk路径一定不要有空格,不然执行相应的cmd命令时会报错) 4.其它需要的软件,mav ...
- Codeforces 746F Music in Car
Music in Car 用两个Set维护一下尺取的过程. #include<bits/stdc++.h> #define LL long long #define fi first #d ...