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. zoj3471 状态压缩dp基础

    /* dp[S]表示状态S下的最大收益,0表示没有了,1表示还在 */ #include<bits/stdc++.h> using namespace std; <<],mp[ ...

  2. win(64位)环境下oracle11g的安装方法

    将压缩文件解压到一个目录中,该目录结构如下: 安装步骤(摘自网络): 1.进入数据库解压目录,双击其中的“setup.exe”文件,稍等片刻出现如下“配置安全更新“界面,取消“我希望通过My Orac ...

  3. jenkins原理

      原理:Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能. 直白的说:这个jenkins是CI ...

  4. OpenCV-Python教程8-图像混合

    一.图片相加 要叠加两张图片,使用cv2.add(),相加两幅图片的形状(高度.宽度.通道数)必须相同.numpy中可以直接用res = img1 + img2相加.但是两者的结果并不相同 impor ...

  5. SqlServer 分页存储过程

    SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE [dbo].[usp_CommonDataResourcePaged ...

  6. 一脸懵逼学习hadoop之HDFS的java客户端编写

    1:eclipse创建一个项目,然后导入对应的jar包: 鼠标右击项目,点击properties或者alt+enter快捷键--->java build path--->libraries ...

  7. ASP.NET Core JWT认证授权介绍

    using JWTWebApi.Models; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetC ...

  8. [转] Form 表单数据处理 简单教程 formidable 使用心得

    入门,高手见笑 表单数据一种是get方式, 另一种是post 方式 1.get方式 对于get方式,node处理起来非常简单 如以下代码: var urlParsed = url.parse(requ ...

  9. [BZOJ3011][Usaco2012 Dec]Running Away From the Barn

    题意 给出一棵以1为根节点树,求每个节点的子树中到该节点距离<=l的节点的个数 题解 方法1:倍增+差分数组 首先可以很容易的转化问题,考虑每个节点对哪些节点有贡献 即每次对于一个节点,找到其第 ...

  10. 基于jquery的页面分屏切换模板

    闲来无事,搞了个页面的分屏效果,先来看下效果: 出于可自定义宽高的目的,屏幕分块由CSS控制,由js控制估计等分模块效果一般. 程序相关说明: HTML结构: <div class=" ...