一、性能下降SQL慢,执行时间长,等待时间长
1、查询语句写的烂,各种子查询、连接,没有索引
2、索引失效
索引分为:单值索引和复合复合索引
3、关联查询太多的join(设计缺陷或不得已的需求)
4、服务器调优及各个参数设置(缓冲、线程数等)
二、常见通用的join查询
1、SQL的执行顺序
①人写
SELECT DISTINCT
<select_list>
FROM
<left_talbe><join_type><right_table> ON <join_condition>
WHERE
<where_condition>
GROUP BY
<group_by_list>
HAVING
<having_condition>
ORDER BY
<order_by_condition>
LIMIT <limit_number>
②机读
FROM
<left_talbe>
ON <join_condition>
<join_type><right_table>
WHERE
<where_condition>
GROUP BY
<group_by_list>
HAVING
<having_condition>
SELECT DISTINCT
<select_list>
ORDER BY
<order_by_condition>
LIMIT <limit_number>
SQL的解析:
2、Join图
三、索引简介
1、索引是什么?
MySQL官方对索引的定义:索引(Index)是帮助MySQL高效获取数据的数据结构。索引的本质是:数据结构。
索引的目的是提高查找效率,可以类比字典的目录。
① 可以简单理解为“排好序的快速查找数据结构”
在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构之上实现高级查找算法。这种数据结构就是索引。
下面是一种可能的索引方式示例:
左边是数据表,最左边是数据记录的物理地址。
为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针。这样就可以运用二叉查找在一定复杂度内找到相应的数据,从而快速的检索出符合条件的记录。
结论:数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这种数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法这种数据结构就是索引。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上。
我们平时说的索引,如果没有特别指明,都是B树(多路搜索树,并不一定是二叉的)结 构组织的索引。其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引。当然,除了B+树这种类型的索引外,还有哈希索引(hash index)等 。
优势:类似大学图书馆建数目索引,提高数据的检索效率,降低数据的IO成本;通过索引对数据进行排序,降低数据排序的成本,降低了CPU的消耗。
2、优势&劣势
劣势:索引实际上也是一张表,该表保存了主键和索引字段,并指向实体表的记录,所以索引也是要占空间的。
虽然索引大大挺高了查询速度,同时却会降低更新表的速度,如对表进行UPDATE、INSERT和DELETE。因为更新表的时候MySQL不仅要保存数据,还要保存索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。
索引只是提高效率的一个因素,如果你的SQL有大量的表,就需要花时间研究建立最优秀的索引,或优化查询语句。
3、MySQL索引分类
单值索引——即一个索引只包含单个列,一个表可以有多个单列索引。
唯一索引——索引列的值必须是唯一的,但允许有空值。
复合索引——即一个索引包含多个列
基本语法:
创建:
CREATE [UNIQUE] INDEX indexName ON mytable(columnname(length))
ALTER mytable ADD [UNIQUE] INDEX[indexName] ON (columnname(length))
删除:
DROP INDEX [indexName] ON mytable
查看:
SHOW INDEX FROM table_name
使用ALTER命令:
有四种方式来添加数据表的索引:
ALTER TABLE tbl_name ADD PRIMARY KEY(column_list):该语句添加一个主键,这意味着索引值必须是唯一的,且不能为null
ALTER TABLE tbl_name ADD UNIQUE index_name(cloumn_list):这条语句创建索引的值必须是唯一的(除了null外,null可能会出现多次)
ALTER TABLE tbl_name ADD INDEX index_name(column_list):添加普通索引,索引值可能出现多次
ALTER TABLE tbl_name ADD FULLTEXT index_name(column_list):该语句指定了索引为FULLTEXT,用于全文索引
4、MySQL索引结构
①、BTree索引
索引原理:
初始化介绍
一颗B+树,浅蓝色的我们称之为磁盘块,可以看到每个磁盘块包含几个数据项(蓝色)和指针(黄色),如磁盘块1包含数据项17和35,包含指针p1,p2,p3。
p1表示小于17的磁盘块,p2表示在17到35之间的磁盘块,p3表示大于35的磁盘块
其真实的数据存储在叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99
非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17,35并不真实存在数据表中。
【查找过程】
如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中使用二分查找确定29在17和35之间,锁定磁盘块1的p2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的p2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的指针p2,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找到29,结束查询,总共三次IO。
真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常高。
②、Hash索引
③、full-text全文索引
④、R-Tree索引
5、哪些情况需要创建索引
主键自动建立唯一索引
频繁作为查询条件的字段应该创建索引
查询中与其他表关联的字段,外键关系建立索引
频繁更新的字段不适合创建索引——因为每次更新不单单是更新了记录还会更新索引,加重了IO负担。
Where条件中用不到的字段不创建索引
单键/组合索引的选择问题?(在高并发情况下倾向创建组合索引)
查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
查询中统计或分组字段
六、哪些情况不需要创建索引
①、表记录太少
②、经常增删改的表:提高了查询表的速度却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表的时候,MySQL不仅要保存数据,还要保存索引文件。
③、数据重复且分布均匀的表字段,因此应该只为最经常查询和最经常排序的数据列建立索引。注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。
假如一个表有10万行记录,有一个字段A只有T和F两种值,
且每个值分布的概率大约为50%那么对这种字段建索引一般不会提高数据库查询速度。
索引的选择性是值索引列中不同值的数据与表中记录的数的比。
如果一个表中有2000条记录,表索引列有1980个不同的值,那么这个索引的选择性就是1980/2000=0.99.
一个索引的选择性越接近1,这个索引的效率就越高。
四、性能分析
1、MySQL Query Optimizer(MySQL自带的查询优化器)
①、MySQL中有专门负责优化SELECT语句的优化模块,主要功能:通过计算分析系统中收集到的统计信息,为客户端请求的Query提供它认为最优的执行计划。(它认为最优的数据检索方式,但不见得是DBA认为是最优的,这部分最耗时间)
②、当客户端向MySQL请求一条Query,命令解析器模块完成请求分类,区别出是SELECT并转发给MySQL Query Optimizer时,MySQL Query Optimizer首先会对整条Query进行优化,处理掉一些常量表达式的预算,直接换成常量值,并对Query中的查询条件进行简化和转换。如去掉一些无用或显而易见的条件、结构调整等。然后分析Query中的Hint信息(如果有),看显示的Hint信息是否完全确定该Query的执行计划。如果没有Hint信息或信息不足以完全确定执行计划,则会读取所涉及对象的统计信息,根据Query进行相应的计算分析,然后再得出最后的执行计划。
2、MySQL的常见瓶颈
CPU:CPU在饱和的时候,一般发生在数据装入内存或从磁盘读取数据的时候。
IO:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
服务器性能的瓶颈:top、free、iostat和vmstat来查看系统的性能状态
3、EXPLAIN
①、是什么(查看执行计划)?
使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL的。分析你的查询语句或表结构的性能瓶颈。
②、能干啥
- 表的读取顺序
- 数据读取操作的操作类型
- 哪些索引可以使用
- 哪些索引被实际使用
- 表之间的引用
- 每张表有多少行被优化器查询
③、怎么玩
Explain+SQL语句(末尾加上 G 显示为竖版)
执行计划包含的信息
④、各字段解释
id:select查询的序列号,包含一组数字,表示查询中执行的select子句或操作表的顺序
—id相同,表示执行优化器执行SQL命令是从上至下的
—id不同,如果是子查询,id序号会递增,id值越大优先级越高,越先被执行
—id相同不同,同时存在;id如果相同可以认为是一组,从上往下顺序执行,在所有组当中,id值越大,优先级越高,越先执行。
select_type:
有哪些
查询的类型,主要用于区别普通查询,联合查询,子查询等的复杂查询。
1、SIMPLE | 简单的select查询,查询中不包含子查询或UNION |
2、PRIMARY | 查询中若包含任何复杂的子部分,最外层查询则被标记为 |
3、SUBQUERY | 在select或者where列表中包含子查询 |
4、DERIVED | 在from列表中包含的子查询被标记为DRIVED(衍生)MySQL会递归执行这些子查询,把结果放在临时表里 |
5、UNION | 若第二个select出现在UNION之后,则被标记为UNION;若UNION包含在from子句的子查询中,外层select将被标记为:DERIVED |
6、UNION RESULT | 从UNION表获取结果的SELECT |
table:显示这一行的数据是关于哪一张表的
type:
访问类型排列
显示查询类型使用了何种类型:
从最好到最差依次是:system>const>eq_ref>ref>range>index>ALL(主要只使用这几种)
system | 表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,也可以忽略不计 |
const | 表示通过索引一次就可以找到,const用于比较primary key或者unique索引,因为只匹配一行数据,所以很快, 如将主键置于where列表中,MySQL就能将该查询转化为一个常量。 |
eq_ref | 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描 |
ref | 非唯一性索引扫描,返回匹配某个单独值的所有行。 本质上也是一种索引访问,它返回所有匹配某个单独值的行, 然而,它可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体。 |
range | 只检索给定范围的行,使用一个索引来选择行,key列显示使用哪个索引。 一般就是在你的where语句中出现了between、<、>、in等的查询, 这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束于另一点,不用扫描全部索引。 |
index | Full Index Scan,index与ALL的区别是index类型只遍历树。这通常比ALL要快,因为索引文件通常比数据文件小 (也就是说虽然all和index都是读全表,但是index是从索引中读取的,而all是从硬盘中读取的) |
ALL | Full Table Scan,将遍历全表以找到匹配的行。 |
一般来说,得保证查询至少达到range级别,最好能达到ref级别。
possible_keys:显示可能被用于这张表的索引,一个或多个,查询涉及到的字段上若存在索引,则该索引将被列出。但不一定被查询实际使用。
key:实际使用的索引。如果为null,则没有使用索引。查询中若使用了覆盖索引,则该索引仅出现在key列表中。
key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确度的情况下,越短越好。
key_len显示的值为索引字段的最大可能的长度,并非实际使用的长度,即key_len是根据表定义计算而得,不是通过表内检索出的。
ref:显示索引的哪一列被使用了,如果可能的话是一个常数。哪些列或常量被用于查找索引列上的值。
rows:根据表的统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数。
Extra:包含不适合在其他列中显示但十分重要的额外信息。(前三个是重点)
—Using filesort:说明MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读数。MySQL中无法利用索引完成的排序操作被称为“文件排序”。
—Using temporary:使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by和分组查询group by。
—Using index:表示相应的select操作使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错!
如果同时出现了Using where,表明索引被用来执行索引键值的查找;如果没有同时出现Using where,表明索引用来读取数据而非执行查找动作。
- 索引覆盖:
—理解一、就是select的数据列只从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件,换句话说就是查询列要被所建的索引覆盖。(也就是建的是什么索引,刚好查询也是这个)
—理解二、索引是高效找行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此它不必读取整个行。毕竟索引叶子节点存储了他们索引的数据;当能够通过读取索引就可以得到想要的数据,那就不要吝啬读取行了。一个索引包含了(或覆盖了)满足查询结果的数据就叫做覆盖索引。
注意:如果要使用覆盖索引,一定要注意select列表中只取出需要的列,不可用select.*
因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降。
—Using where:表明使用了where过滤
—Using join buffer:表示使用了连接缓存
—Impossible where:where语句的值总是false,不能用来获取任何元素。
—select tables optimized away:在没有Group By子句的情况下,基于索引优化MIN/MAX操作或者对MyISAM存储引擎优化count(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。
—distinct:优化distinct操作,在找到第一个匹配元素后即停止找同样值的动作。