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语句优化---索引的更多相关文章

  1. Mysql基础(二):MySQL之存储引擎

    目录 MySQL之存储引擎 1.MySQL存储引擎介绍 2.MySQL结构 3.MySQL存储引擎分类 4.存储引擎的使用 5.总结 MySQL之存储引擎 1.MySQL存储引擎介绍 MySQL中的数 ...

  2. MySQL基础(二)——DDL语句

    MySQL基础(二)--DDL语句 1.什么是DDL语句,以及DDL语句的作用 DDL语句时操作数据库对象的语句,这些操作包括create.drop.alter(创建.删除.修改)数据库对象. 2.基 ...

  3. MYSQL基础笔记(二)-SQL基本操作

    SQL基本操作 基本操作:CRUD,增删改查 将SQL的基本操作根据操作对象进行分类: 1.库操作 2.表操作 3.数据操作 库操作: 对数据库的增删改查 新增数据库: 基本语法: Create da ...

  4. MYSQL基础笔记(三)-表操作基础

    数据表的操作 表与字段是密不可分的. 新增数据表 Create table [if not exists] 表名( 字段名 数据类型, 字段名 数据类型, 字段n 数据类型 --最后一行不需要加逗号 ...

  5. MYSQL基础笔记(一)

    关系型数据库概念: 1.什么是关系型数据库? 关系型数据库:是一种建立在关系模型(数学模型)上的数据库 关系模型:一种所谓建立在关系上的模型. 关系模型包含三个方面: 1.数据结构:数据存储的问题,二 ...

  6. MYSQL基础笔记(六)- 数据类型一

    数据类型(列类型) 所谓数据烈性,就是对数据进行统一的分类.从系统角度出发时为了能够使用统一的方式进行管理,更好的利用有限的空间. SQL中讲数据类型分成三大类:1.数值类型,2.字符串类型和时间日期 ...

  7. MYSQL基础笔记(四)-数据基本操作

    数据操作 新增数据:两种方案. 1.方案一,给全表字段插入数据,不需要指定字段列表,要求数据的值出现的顺序必须与表中设计的字段出现的顺序一致.凡是非数值数据,到需要使用引号(建议使用单引号)包裹. i ...

  8. MYSQL基础笔记(五)- 练习作业:站点统计练习

    作业:站点统计 1.将用户的访问信息记录到文件中,独占一行,记录IP地址 <?php //站点统计 header('Content-type:text/html;charset=utf-8'); ...

  9. MySQL基础配置之mysql的默认字符编码的设置(my.ini设置字符编码) - 转载

    MySQL基础配置之mysql的默认字符编码的设置(my.ini设置字符编码) MySQL的默认编码是Latin1,不支持中文,那么如何修改MySQL的默认编码呢,下面以设置UTF-8为例来说明. 需 ...

随机推荐

  1. hiho1460 rmq模板题

    好久没做rmq的题了,今天写了一遍,感觉打表有点像区间dp /* 给定长为n的字符串,要求在字符串中选择k个字符, 选择的子系列字典序最小 因为选择k个字符,那么就是去掉n-k个字符 那么[1,n-k ...

  2. Repair the Wall

    问题 : Repair the Wall 时间限制: 1 Sec  内存限制: 128 MB 题目描述 Long time ago , Kitty lived in a small village. ...

  3. HTML添加图像和超链接

    添加图像 <img src="图像的文件地址" alt="加载失败" height="350" width="350&quo ...

  4. ThreadLocal用法详解和原理

    一.用法 ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量. 1.Thr ...

  5. 一脸懵逼学习Hadoop中的MapReduce程序中自定义分组的实现

    1:首先搞好实体类对象: write 是把每个对象序列化到输出流,readFields是把输入流字节反序列化,实现WritableComparable,Java值对象的比较:一般需要重写toStrin ...

  6. [转] 谈谈JS中的函数节流

    函数节流的目的 从字面上就可以理解,函数节流就是用来节流函数从而一定程度上优化性能的.例如,DOM 操作比起非DOM 交互需要更多的内存和CPU 时间.连续尝试进行过多的DOM 相关操作可能会导致浏览 ...

  7. spring、springmvc、springboot、springcloud

    Spring 最初利用“工厂模式”( DI )和“代理模式”( AOP )解耦应用组件.大家觉得挺好用,于是按照这种模式搞了一个 MVC 框架(一些用 Spring 解耦的组件),用开发 web 应用 ...

  8. 【AtCoder】Tenka1 Programmer Contest 2019

    Tenka1 Programmer Contest 2019 C - Stones 题面大意:有一个01序列,改变一个位置上的值花费1,问变成没有0在1右边的序列花费最少多少 直接枚举前i个都变成0即 ...

  9. train_test_split数据切分

    train_test_split 数据切分 格式: X_train,X_test, y_train, y_test =cross_validation.train_test_split(train_d ...

  10. HTML自定义滚动条(仿网易邮箱滚动条)转载

    它是使用CSS中的伪元素来实现的,主要由以下三个来完成: 1. -webkit-scrollbar:定义滚动条的样式,如长宽. 2. -webkit-scrollbar-thumb:定义滚动条上滑块的 ...