mysql高性能6章总结(下) mysql查询优化

6.5查询优化器的局限性

mysql优化器是有局限性的,有时需要我们改写查询以提高效率。

6.5.1关联子查询

子查询是mysql一个很不效率的地方。

这一节首先我们需要了解一下相关子查询:内外部查询间有一个关联条件,类似于join中的等值条件

书中说尽量不要使用in来写相关子查询。推荐使用exists与join的方式来替代。

我感到比较迷惑

书中说mysql优化器会将用in非关联子查询解析为exists关联子查询。那么用exists优化in有什么意义呢?

我做的两个测试中exists与in的explain结果是一致的?

Exists与in的不同通过英文的简单理解就可以明白,exists是存在,返回一个bool值,只要

子查询查到有一个值成立就立马返回,能减少子查询的消耗。

关联子查询的效率是很低的,每一条外部查询的数据行都要让内部查询执行一次。而关联查询join则是一次性将n*m的结果全部罗列,还能通过where条件筛选,更容易优化。(自己的理解)

如何用好关联子查询

子查询并不一定是效率不好的,在合适的时候它也可以使用。具体情况应该explain以后再看结果。

6.5.2union的限制

一些情况union无法将where条件下推到内层,例如分语句的limit20。Union的分语句不可能有超过20条被留下,limit20应该是可以下推的,我们需要手动说明。

这里要注意从临时表去除数据顺序是不固定的。

6.5.3索引合并优化

5.0以后索引已经可以通过索引合并和交叉过滤的方式来定位目标行。

类型列中显示为index_merge

6.5.4等值传递

在关联中对于关联条件的筛选可以下推到各个子查询中。

6.5.5并行执行

Mysql无法利用多核特性并行查询。

6.5.6哈希关联

Mysql不支持哈希关联,不过可以通过自实现的哈希索引来实现哈希关联。

6.5.7松散索引扫描

Mysql不支持松散索引扫描。5.0后在某些条件下可以实现松散索引扫描

Extra列显示 using index for group-by

6.5.8最大值最小值优化

Mysql并不能使用索引的来获取极值,即使索引是有序的,他依然进行全表扫描。有时候我们可以用limit加上指定索引来手动指定返回摸个索引的第一行或者前几行。这个问题myisam解决的不错啊。

6.5.9在同一个表上查询和更新

Mysql不允许在同一张表中同时进行查询与更新。

可以注意奥上文中关联子查询是需要两条查询进行嵌套的,当这两条sql一条为更新,一条为查询是就会报错。这时可以采用关联子查询一节中的处理方式将其转换为join查询的。

6.6查询优化器提示(hint)

这一节给出了dba控制sql的执行计划的方式,即hint。这里我不在罗列hint。在使用hint时要通过详细的测试hint是否能带来效率的提升。(delayed, straight_join, use index ,force index, ignore index)

6.7优化特定类型的查询

6.7.1优化count()

Mysql的count()是不统计空值的。只有才确定一行一定不为空的情况下才能统计行数。

Myisam的count()并不总是非常高效的只有没有where时myisam才能将存储的count(col)

值优化为count(*)才能高效的实现count(*)

简单的count优化

在myisam中我们知晓count(col)的代价很小,但我们需要一个count where 的数据时,可以在合适情况下采用总数减去不需要的行数的办法获取count。

使用近似值

这一类的代表为innodb这样的引擎,即通过explain执行计划来估算查询的行数。

跟复杂的优化

可以使用汇总表来记录时常要用到的统计值来优化。

6.7.2优化关联查询

  1. 关联字段有索引很重要的,但是只有内部查询需要索引,因为外部查询是用不到该列的索引的。
  2. 确保任何的group by与order by只涉及一个表中的列,这样mysql才能使用索引来优化这个过程。
  3. Mysql版本的升级可能会影响关联查询的执行方式

6.7.3优化子查询

尽量使用关联查询(醉了前面说那么多子查询还行后面还是让你用关联查询)

6.7.4优化group by和order by

Group by默认会按照分组字段进行排序,如果不需要排序,可以使用 order by NULL 来禁止排序。需要特定顺序也可以使用desc,asc。

Group by 语句中不于分组字段直接关联的都是无意义的返回值,不同的环境,可能有不同的非关联返回值。

Group by with rollup

超级聚合,可以将分组的结果进行一次超级聚合,即聚合后的总计。在分组项有多项式会有更多的组合。不过Group by with rollup 并不是处理此类需求的一个号的解决方式,最好还是通过程序来解决。

6.7.5优化limit分页

Limit能够减少返回的结果集,所以尽可能早的使用order by与limit能有效减少io次数。

对于offset(取数据的起始值)关键字,我们应该尽量不使用,可以用自定义书签的方式,来访问偏后的数据,但是这样往往需要数据字段中有一个可以预估增长方式的值。

6.7.6优化sql_calc_found_rows

这个hint要求sql扫描所有满足where条件的行,再根据limit抛弃不需要的行。这样的需求往往是我们需要提供一个总的记录数才需要给出该hint,

6.7.7优化union查询

一句话用union all而不用union。

使用union会给要求加入结果集的数据唯一,而对所有数据行进行唯一性检查,这样额外的开销。     当然跟好的做法是不使用union而将各个查询的结果返回给客户端,让客户端来实现union(union的结果集会保存在一个临时表中,返回给客户端有需要将临时表中的数据读取出来)。

6.7.8静态查询分析

通过工具为所有的查询语句做一个体检。

6.7.9使用用户自定义变量

使用用户自定义变量可以保存一些查询结果,用来在sql中实现应用程序中的逻辑而不是通过关联查询等等方式来体现逻辑。是一种解决方式,不过在存储过程中使用的比较多。

  1. 优化排名语句

自定义变量可以作为左值(可以赋值)。

可以通过自定义变量来自实现类似count的功能,甚至可以对’count’的过程加上条件!来实现排名功能!(具体事例书中查看)

  1. 避免重复查询刚刚插入的数据

标题很迷惑人,其实就是但你需要将你刚刚插入的数据返回时!Mysql没有相应的语法来一次性实现这样的功能(postgreSQL提供update return语法)。我们必须使用一条insert以及一条select两条语句来实现。

而使用变量保存我们刚刚跟新的值的就可以免于select语句的执行。

  1. 统计更新与插入数量

在Insert on duplicate key update 可以计算update的记录数,通过mysql返回的总行数可以知道insert的记录数。(Insert on duplicate key updatec1=values(c1)+0*(@x:=@x+1))

  1. 确认取值的顺序

但语句使用到using temporary与using filesort时可能会导致变量的值。可以通过一个字符处理函数将变量赋值包括进去。例如least()。

  1. 编写偷懒的union

可以通过一个变量的使用来避免第一个union分支已经找到数据的情况下,后续分支依然要执行的情况。

  1. 用户自定义变量的其他用处

总结:起来就是查询进行中的自定义单条数据判断条件控制。

6.8案例学习

6.8.1使用mysql构建一个对列表

  1. 应该尽量避免select for update,而应该使用在sql中connection_id()来判断有没有用户正在访问这条记录。
  2. 如果逻辑允许一个事务中应该先update再select会更好。

6.8.2计算两点间的距离

这个例子告诉我们。

  1. 少做事,用尽量少的逻辑来实现功能。(指的是少用sql实现)
  2. 能否使用索引会严重影响查询的效率,不过也不是无脑索引
  3. 尽可能的让应用程序来完成计算的部分。

体会:

第六章的内容其实就是介绍了sql的优化,以及我们在配合程序编写sql时应该注意什么?

在几个月的工作中,发现sql部分优化往往都不是前期开发的关键,因为,应用程序的处理会让我们比较轻易的将一整条sql拆分成多条简单的sql,在配合合适的索引,sql往往都不会有太大的问题。。但是也许程序发布以后流量打起来sql的问题就来了

上一篇:《面向对象程序设计》第二次作业(1)(A+B问题)


下一篇:浅谈JavaScript的面向对象程序设计(三)