Mysql比知必会系列教程(八) --------mysql查询优化

Mysql查询优化

查询优化是查询编译过程中的一个环节,他负责把以某种高级语言写出的数据操作语句转成一个更加详尽的过程化的操作符序列,这个序列就是所谓的查询计划

select ->查询解析器–>查询优化器—>代码生成解释器---->查询处理器----->得到查询结果

进行查询优化的4个基本技术:

  • 基于开销的优化
  • 启发式优化
  • 语义优化
  • 参数优化

mysql是一个混合使用多种优化技术的数据库系统。mysql查询优化是围绕“选取–投影–联结”策略进行的。综合了基于开销的优化器和启发式优化器的优点,生成查询计划相随较少。然后在利用基于开销的优化技术从中选出一个最短的执行路径。

使用索引

索引可以加快查询的技术有很多,其中最重要的是索引,通常能够使得查询速度照成差异化。当查询速度很慢时,添加上索引后就能够迅速解决问题。

索引可以提高搜索效率的第一个原因就是我们可以得知匹配数据行在什么位置结束,从而跳过其余部分,

mysql使用索引的方式有以下几种:

  • 在查询操作中把where子句所给出的条件相匹配的数据行尽快找出来,二是在关联操作中把与其他数据表的数据行相匹配的数据行尽快找出来。
  • 对于使用min()或max()函数的查询,如果数据列带索引,那么它的最小值和最大值能够被迅速找到而不用通过逐行检查的方法查找
  • mysql经常使用索引来迅速完成order by子句和group by子句的分类和分组凑在哦。
  • 有时mysql可以通过使用索引来避免为一个查询整体读取数据行。

如果不需要某个特定的索引加快查询速度就不要创建他。

挑选索引

  • 尽量为用来搜索,分类或分组的数据列编制索引,不要为输出显示列编排索引。最适合有索引列的数据列是那些在where子句出现的数据列,在连接子句中给出的数据列,或者order by ,group by 子句出现的数据列。
  • 根据select关键子输出的数据列最好不要做索引。

select

​ col_a <-----a candidate

from

​ tab1 left join tb2

​ on tab1.col_b = tab12.col_c <-- candidates

where

​ col_d=expr <—a candidate

  • 综合考虑各数据列的维度。

    数据列的维度等于他所能容纳的非重复值的个数。数据列的维度越高,他包含的独一无二的值就越多,重复值就越少,索引的效果就越好。

    查询优化程序确定出某个数值在数据表中的数据行中出现的频率超过30%时查询优化就会跳过索引。进行全表扫描。现如今的优化器更复杂,能够把其他因素也考虑过来,所以这个百分比已经不再是mysql决定进行一次扫描而不使用一个索引的唯一依据了

  • 对短小的值进行索引。

    尽量选用小的数据类型,比如你的数据没有一个比25个字符更长,就不要选用char(100).比较短小的值可以从以下几个方面提高索引的处理性能。

    短小的值可以让操作更快完成,加快索引查找速度。

    短小的值可以让索引的“体积”更小,减少磁盘I/O活动

    短小的键值意味着缓存里的索引可以容纳更多的键。

  • 为字符串值的前缀编排索引。

  • 充分利用最左边的前缀。

    当创建一个n个数据列的复合索引时,复合索引 相当于n个索引,因为索引的最左边的数据列集合能够用于匹配数据行,这样的一个集合称为:最左边索引。

  • 适可而止,不要建立过多的索引。

  • 让索引的类型与你打算进行比较操作的类型保持匹配。

  • 利用慢查询日志找出性能劣质的查询。

查询优化程序

EXPLAIN SELECT * from domains_domain where isDeleted=0

id selete_type table partitions type possible_keys key-len ref rows filtered Extra
1 SIMPLE domains_domain all 75203 10 using where
Explain
id
  • id相同,执行顺序由上至下
  • id不同,如果是子查询,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 > eq_ref > ref > range > index > all

全版:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > all

一般保证查询至少达到range级别,最好能达到ref

  • all:full table scan 将遍历全表以找到匹配的行
  • index:full index scan,index与all区别为:index类型只遍历索引树。这通常比all快,因为索引文件通常比数据文件小。(也就是说虽然all和index都是读全表,但index是从索引中读取而all是从硬盘中读)
  • range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引,一般就是在你的where语句中出现了between、<、>、in等的查询。这种范围扫描,索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束于另一点,不用扫描全部索引。
  • ref:非唯一性索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
  • eq_ref:唯一索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描。
  • const:表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快。如将逐渐置于where列表中,MySQL就能将该查询转换为一个常量
  • system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,可以忽略不计
  • null
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操作中使用了覆盖索引,避免访问了表的数据行,效率不错。如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。
  • using where:表示使用了where过滤
  • using join buffer:使用了连接缓存
  • impossible where:where字句的值总是false,不能用来获取任何元祖
  • select tables optimized away:在没有groupby子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化count(*)操作,不必等到执行阶段再进行计算,查询执行计划生成阶段即完成优化。
  • distinct:优化distinct操作,在找到第一匹配的元组后即停止找同样值的动作。
查询优化器工作原理

查询优化器主要的目的是只要对可能就要使用索引,并且要使用条件最严格的索引来尽可能多,尽可能快地排除那些不符合索引条件的数据行。

对数据表进行分析

这将生成关于索引值分情况的统计数据,他们可以帮助优化器对索引的使用效果做出更准确的评估。如果数据表经常更新就应该对其不时地进行分析;如果某数据填充好就不在发生变化,只需在加载后对其做一次分析就够了。

使用Explain语句来验证优化器操作。

expain语句会告诉对定的查询有没有使用索引

向优化器提供提示或在必要时屏蔽之

在连接时可以对数据表名字后面利用 force index ,use index或ignore index限定词告诉服务器想使用哪些索引。

尽量使用数据类型相同的数据列进行比较

对带有索引的数据列进行比较时,如果特闷数据类型相同,查询性能就会高一些,如果查询类型不同,查询性能就会低一些。

使带索引的数据列在比较表达式中单独出现

如果你在函数调用中使用数据列,或者将数据列作为算术运算表达式中很复杂的项目的一部分,mysql将不能使用索引。因为它必须要对每个数据列计算出表达式的值。

这个索引不能使用:select * from mytab where Year(data_col) <1990; 这时比较的时必须计算出数据列的值。完成整个计算结果数据列data_col的索引就不能使用,会进行全表扫描,必须使用一个准确的日期表示方法 where data_col<‘1990-01-01’

这个索引不能使用:where TO_DAYS(date_col) - TO_DAYS(curDate()) < cutoff 因为数据列的每行都要检索才能计算出TO_DAYS(date_col)的值

这个索引不能使用:where TO_DAYS(date_col) <cutoff +TO_DAYS(curDate()) 因为数据列的date_col仍然会出现在 函数调用中

这个索引能使用:where date_col < DATE_ADD(currdate(), INTERVAL cutoff DAY) 这样是让日期值与数据列比较不再转换成天数,可以使用索引

不要在like 模式的开始位置使用通配符

where col_name like ‘%string%’ 语句是正确的,但不要出于习惯将符号“%”放置字符串两侧、

匹配“MAC姓氏开头的”macGre mascD

where col_name like ‘string%’ 时优化程序看到了模式是字符开始部分会使用索引行,这样好像是就是 where las_name >=‘mac’ and last_name <‘mad’

这种优化不能使用在Regexp操作表达式中,regexp表达式不能被优化

利用优化器的长处

Mysql支持联结查询和子查询,联结查询优化效果比对子查询的优化效果更好。

试验各种查询的变化的格式,而且多次运行他们

当试验一个查询的变化2格式时每一种方式都要多运行几次。

避免过多的使用Mysql的自动类型转换功能

mysql能够实现自动类型转换,但是如果能避免这些转换,

select * from mytb1 where num_col= 4;

select * from mytb1 where num_col=‘4’ ; 包含数据转换转换,如果数据列num_col有索引,那么含有类型转换的比较有可能阻止索引得到试用、

提高查询效率而挑选数据类型

尽量使用数值操作,少使用字符串操作

数值运算比字符运算快的多。数值之间的比较只需要一个操作就可以完成,而字符串之间的比较一般需要进行多次字节与字节或字符与字符的比较才能完成,而且字符串比越长,比较次数就越多。

如果小类型够用,就不要选用大类型

小类型比大类型的处理速度更快,字符串处理为时与他们得长度呈正比关系。选用小的数据类型可以使得整个数据表更小,从小减少磁盘读写开销,对于数据列有索引的,使用较短的数据值还将进一步提升。短索引值处理速度比长索引值的处理更快。

如果能选择数据行的存储格式,就应该尽量选用最合适的存储引擎格式
尽量把数据列声明为not null

数据列声明not null 时,对他的处理可以更快的完成。查询期间不再会检查数据列值是不是null了。

考虑使用enum数据列
利用Procedure analyse()语句

select * from tab1_name procedure analyse()

对易产生碎片的数据表进行整理
把数据压缩到blob或text数据列里
使用人造索引

根据数据表里的其他数据列计算出一个散列值并将它来另外保存一个数据列然后通过搜索散列值去找,这里只适合精确匹配查询。Md5(),SHA1() CRC32()函数生成散列值

尽量避免对很大的blob或text值进行检索

当不需要查询出大的字段时建议不要查出。

把blob或text数据列剥离到单独的数据表中

调度和锁定问题

mysql的默认调度策略综述如下:

  • 写入比读取有更高的优先权
  • 对数据表的写操作必须按照“写”请求先来后到的顺序一个一个地进行。
  • 对同一个数据表进行读操作可以同时进行。

mysql提供了一些语句修饰符来改变他的调度策略。一是在delete,insert,load data, replace和update语句使用low_priority关键字,二是在select和insert语句使用high_priority关键字,三是在insert和replace语句中使用delayed关键字。

low_priority和high_priority限定符只对支持数据表锁定功能的存储引擎。有效果,delayed限定符在myISAM,MEMEORY,ARCHIVE和BLACKHOLE存储引擎上都可以使用。

改变语句优先级

low_priority关键字影响delete,insertmload_data,replace和update语句的调度。

写等待:当一个数据表正在读取时来了一个写的操作,则写操作就会等待读操作完成之后。默认的调度策略是写入者比读取者优先

对于high_priority关键字的select查询,情况也是类似。允许select查询操作插入正在等待的写入操作之前。即使通常情况下写入操作有较高的优先。另外一个高优先的select将先于普通的select语句执行。

使用延迟插入

delayed修饰符用在insert和replace语句中,当delayed请求到达数据表时,服务器将数据行排序并且迅速地返回到客户程序的状态,从而使客户程序可以在数据行被插入之前进行。

使用并发插入

并发插入应注意以下问题。

  • 不要在insert语句中使用low_priority修饰符,这会时insert语句总是锁定读取者,从而导致并发插入不能完成。
  • 那些需要显式锁定数据表但又想允许并发插入的读取者应该使用lock tables … read local 而不是lock tables …read。关键字local会使你得到一个允许并发插入的锁。因为它只用于在数据表中已经存在的数据行并不锁定正在被添加到数据表的末尾的新行,
  • 应该给load data嘉善concurrent限定符,让给数据表上的select语句在该数据表正在加载数据的同时仍可以执行。

猜你喜欢

转载自blog.csdn.net/qq_37256896/article/details/106966395
今日推荐