1.导致性能下降SQL慢执行时间长,等待时间长的原因
一般有查询语句写得烂、索引失效、关联太多join(设计缺陷不得已的需求)、服务器调优以及各个参数配置,缓存,线程数。
2.索引介绍
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。你可以简单理解为排好序的查询数据结构。一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储再磁盘上,我们平常所说的索引,如何没有特别的指明,都是B数(多路搜索树,并不一定是二叉的)结构组织索引,其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认都是使用B+数索引,统称索引,当然除了B+数这种类型的索引外还有(hash索引等)。
索引的优势:类似大学图书馆建书目索引,提高数据检索的效率,降低数据IO成本,通过索引列对数据排序,降低数据排序的成本,降低CPU的消耗。
索引的劣势:实际上索引也是一张表,该表保存了主键和索引字段,并指向实体表的记录,所以索引也是要占空间的。虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅 要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息索引只是提高效率的一个因素,如果你的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\G;
需要创建索引的情况:
①主键自动建立唯一-索引。
②频繁作为查询条件的字段应该创建索引。
③查询中与其它表关联的字段,外键关系建立索引。
④频繁更新的字段不适合创建索引-因为每次更新不单单是更新了记录还会更新索引。
⑤Where条件里用不到的字段不创建索引。
⑥单键/组合索引的选择问题,who? (在高并发下倾向创建组合索引)。
⑦查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度。
⑧查询中统计或者分组字段。
那些情况不创建索引:
①表记录太少。
②经常增删改的表。
③数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的数据列建立索引。注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。
3.性能分析
Mysql Query Optimizer
①Mysq|中有专门负责优化SELECT语句的优化器模块,主要功能:通过计算分析系统中收集到的统计信息,为客户端请求的Query提供他认为最优的执行计划(认为最优的数据检索方式,但不见得是DBA认为是最优的,这部分最耗费时间)。
②当客户端向MySQL请求一条Query,命令解析器模块完成请求分类,区别出是SELECT并转发给MySQLQUERY Optimizer时, MySQL QUERY Optimizer 首先会对整条Query进行优化,处理掉一 些常量表达式的预算,
直接换算成常量值。并对Query中的查询条件进行简化和转换,如去掉一些无用或显而易见的条件、结构调整等。然后分析Query中的Hint信息(如果有),看显示Hint信息是否可以完全确定该Query的执行计划。如果没有Hint或Hint信息还不足以完全确定执行计划,则会读取所涉及对象的统计信息,根据Query进行写相应的计算分析,然后再得出最后的执行计划。
MySQL常见瓶颈
①CPU:CPU再饱和的时候一般发生在数据装入内存或从磁盘上读取数据的时候。
②IO:磁盘I/O瓶发生在装入数据远大于内存容量的时候。
③服务器硬件的性能瓶颈: top free iostat 和vmstat来查看性能状态
Explain
explain:使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句,分析你的查询语句或是表结构的性能瓶颈.
功能:
①表的读取顺序。
②数据的读取操作的操作类型。
③那些索引可以使用。
④那些索引被实际使用。
⑤表之间的引用。
⑥每张表有多少行被优化器查询。
使用:Explain+SQL语句
执行计划包含的信息:
下面对上面包含的索引信息解释
id:select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序。
它有三种情况:①id相同,执行顺序由上至下。
②d不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行。
③id相同不同,同时存在。
select_type:查询的类型,主要是用于区别,普通查询,联合查询,子查询等的复杂查询。
一般有下面几种:
①SIMPLE:简单的 select 查询,查询中不包含子查询或者UNION。
②PRIMARY:查询中若包含任何复杂的子部分,最外层则被标记为。
③SUBQUERY:在SELECT或WHERE列表中包含了子查询。
④DERIVED:在FROM列表中包含子查询被标记为DERIVED (衍生) MYSQL会递归执行这些子查询,把结果放在临时表中。
⑤UNION:若第二个SELECT出现在UNION之后,则被标记为UNION,若UNION包含在FROM子句的子查询中,外层SELECT将被标记为DERIVED。
⑥UNION RESULT:从UNION表获取结果的SELECT。
table:显示这一行的数据是那张表的。
type:访问类型排列。从最好到最差的依次是system>const>e1_ref>ref>range>index>ALL。
①system:表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不记。
②const:或者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 Tbale Scan 将遍历全表以找到匹配的行。
备注:一般来说,得保证查询至少达到range级别,最好能达到ref。
possible_keys
显示可能应用在这张表中的索引,一个或多个查询设计到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用。
key:实际使用的索引,如果为NULL,则没有使用索引,查询中若使用了覆盖索引,则该索引仅出现在key列表中.
key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引长度,在不损失精确性的情况下,长度越短越好.
ken_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,而不是通过表内检索出的.
ref:显示索引的那一列被使用了,如果可能的话,是一个常数,那些列或常量被用于查找所有列上的值.
row: 根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要的读取行数.
Extra:包含不适合在其他列中显示但十分重要的额外信息.
①Using filesort:说明mysql会对数据使用一个外部索引排序,而不是按照表内的索引顺序进行读取,MySQL中无法利用索引完成的排序操作称为,’文件排序‘。
②Using temporary:使用临时表保存了中间结果,MySQL在对查询结果排序时时使用临时表,常见于排序order by 和分组查询group by。
③Using index:表示相应的select操作中使用了覆盖索引(Covering Index) 避免访问了表的数据行,效率不错,如果同时出现using where 表明索引被用来执行索引键值的查找。如果没有出现using where 表明该索引用来读取数据而非执行查找动作.
单表索引优化案例
优化前:
很明显,type是ALL,即最坏情况。Extra里面还出现了Using filesort,也是最坏的情况。优化是必须的。
建索引:
再看以下优化情况:
type变成了range,这是可以忍受的。但是extra里使用Using filesort仍是无法接受的,但是我们已经建立了索引,为啥没用呢,这是因为按照BTree索引的工作原理,先排序category_id,如果遇到相同的category_id则再排序comments,如果遇到相同的comments则再排序views,当comments字段再联合索引里处于中间位置时,因为comments>1条件是一个范围值,Mysql无法利用后面的views部分进行检索,即range类型字段后面的索引失效。
删第一次建的索引,第二次建索引
建完索引后再看一下优化情况
可以看到,type变为ref,Extra中的Using fileSort也消失了,结果非常理想。
双表索引优化案例
没有建索引之前, type为All
添加索引优化
再次查看优化情况
可以看到第二行的type变为,rows也变成了优化比较明显。这是由左连接决定的,left join条件用于如何从右表进行搜索行,左边一定都有,所以右边是我们的关键点,一定需要要建立索引。
索引失效情况
①最佳左前缀法则:如果索引了多列,要遵守最左前缀法则,指的是查询从索引的最左前列开始并且不跳过索引中的列。
②不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描。
③存储引擎不能使用索引中范围条件右边的列。
④ 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*。
⑤mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描。
⑥is null ,is not null 也无法使用索引。
⑦like以通配符开头('%ab...)mysq|索引失效会变成全表扫描的操作圆。
⑧字符串不加单引号索引失效。
⑨少用or.用它来连接时令索引失效。
一般性建议
①对于单键索引,尽量选择针对当前query过滤性更好的查询。
②在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。
③在选择组合索引的时候,当前选择可以能够包含当前query中的where子句中更多字段的索引。
④尽可能通过分析统计信息和调整query的写法达到选择合适的索引的目的。