MySQL查询优化

更新日期: 2019-03-23阅读: 4.6k标签: 优化

MySQL的查询怎么才能更快,更合理?除了加索引还有什么可以学习的呢?


原理

要想更好地学习某样东西,从其原理和运作方式入手更容易掌握。道理你们都懂,我就不废话了。

MySQL发送查询请求,到底做了什么工作?

下图是MySQL查询执行流程图


  1. 客户端发送一条查询给服务器。
  2. 服务器先检查查询缓存,如果命中了缓存,则立刻返回查询在缓存中的结果。否则会进入下一个阶段。

3.服务端进行SQL解析、预处理、再由优化器生成对应的执行计划。
4.MySQL根据优化器生成的执行计划,调用存储引擎的api来执行查询。
5.将结果返回给客户端。

是什么导致MySQL查询变慢了?

对于MySQL,最简单的衡量查询开销的三个指标如下:

  • 响应时间
  • 扫描的行数
  • 返回的行数

没有哪个指标能够完美地衡量查询的开销,但它们大致反映了MySQL在内部执行查询时需要访问多少数据,并可以大概推算出查询运行的时间。

查询慢的原因基本都是:我们的不合理操作导致查询的多余数据太多了
常见原因有以下:

1.查询不需要的记录。
2.多表关联时返回全部列
3.总是取出全部列


常用优化技巧

1.用索引

最简单且见效最快的方式就是给你的条件加索引(主键索引,普通索引,唯一索引等)。注:索引是要另开辟一块空间存储的,所以不能不要钱滴都加索引。

2.关联子查询

MySQL的子查询实现是非常糟糕的。比如下面的

SELECT * FROM book WHERE book_id IN (SELECT book_id FROM author WHERE author_id = 1)

MySQL对IN()列表中的选项有专门的优化策略,一般会认为MySQL会先执行子查询返回所有包含author_id 为1的book_id。

或许你想MySQL的运行时这样子的:

SELECT GROUP_CONCAT(book_id) FROM author WHERE author_id = 1
SELECT * FROM book WHERE book_id IN (1,21,3,45,656,766,213,123)

但是,MySQL会将相关的外层表压到子查询中的,就是下面的样子:

SELECT * FROM book WHERE EXISTS 
(SELECT * FROM author WHERE author_id = 1 AND book.book_id = author.book_id)

原因:因为子查询需要book_id ,所以MySQL认为无法先执行这个子查询,而是先对book 进行全表扫描,然后再根据book_id进行子查询。具体可以EXPLAIN该SQL进行分析。

建议:
1.使用左外连接(LEFT OUTER JOIN)代替子查询

SELECT * from book LEFT OUTER JOIN author USING(book_id) WHERE author.author_id = 1

影响因素:还有数据表放的位置等,具体应用场景就只能你自己explain该语句对比哪种性能比较好点。

2.确保ON或者USING子句的列上有索引
在创建索引的时候就要考虑到关联的顺序。

3.UNION使用

如果希望UNION的各个子句能根据LIMIT只取部分结果集,或者希望能够先排好序再合并结果集的话。
第一个例子:会将author 表和user 表两个表都存放到一个临时表中,再从临时表中取出前20条。

(SELECT first_name FROM author ORDER BY last_name)
UNION ALL
(SELECT first_name FROM user ORDER BY last_name)
LIMIT 20

对比上面的这样子,就有很大的改善了。

(SELECT first_name FROM author ORDER BY last_name LIMIT 20)
UNION ALL
(SELECT first_name FROM user ORDER BY last_name LIMIT 20)
LIMIT 20

4.最大值和最小值

比如:
求最小值
第一种方案:

SELECT MIN(id) FROM article WHERE author = 'zero'

第二种方案:

SELECT id FROM article USE INDEX(PRIMARY) WHERE author = 'zero' LIMIT 1

和第一种方案的对比,效果其实是一样的,但是它们的性能略有不同,具体还请自己具体场景分析,择优选择。

5.COUNT()查询

比如如果想统计文章id大于25的数量,可以如下:

EXPLAIN SELECT COUNT(*) FROM article WHERE id >25

另外一种思路:可以先查询文章总数,减去小于等于25的数量。仅仅提供思路,具体效果还是你具体情况,自己比较,择优选择。

EXPLAIN SELECT (SELECT COUNT(*) FROM article) - COUNT(*) FROM article WHERE id <=25

题外话:
如果需要区分不同颜色的商品数量时,可以如下做法:

seelct count(color = 'blue' OR NULL) as blue,COUNT(color = 'red' OR NULL) AS RED FROM items

6.GROUP BY和DISTINCT

它们的优化最有效的方法就是用索引来。
但是GROUP BY有时候用得不对,索引是会失效的。
比如:把两个单独的索引合并成一个组合索引,即把where条件字段的索引和group by的分组字段索引组合成一个。

解决方法:参考这篇函数索引

7.limit分页

下面这条查询,非常常见。

select film_id,description from film order by title limit 50,5;

但是如果这个表很大的时候,那么这个50变成100654这样子的话,这里MySQL就要扫描100654+5条数据,然后丢弃100654条,仅仅去最后5条。
一种思路:

select film_id,description from film inner join (select film_id from film order by title limit 50,5) aslim USING(film_id);

该思路是通过延迟关联将大大提升查询效率,它让MySQL扫描尽可能少的页面。获取需要访问的记录后,再更加关联列会原表查询所需要的所有列。以上并不一定符合你,具体还需explain对比择优使用。

小结:
总体来说都是围绕着尽量少全表扫描,尽量使用索引进行优化。
最后往往是要自己在实际场景多用explain分析是否有更好的sql解决方案。


索引会失效的场景

1.隐式转换导致索引失效.
这一点应当引起重视.也是开发中经常会犯的错误. 由于表的字段tu_mdn定义为varchar2(20),但在查询时把该字段作为number类型以where条件传给Oracle,这样会导致索引失效.

 错误的例子:select * from test where tu_mdn=13333333333;
 正确的例子:select * from test where tu_mdn='13333333333';

2. 对索引列进行运算导致索引失效
所指的对索引列进行运算*包括(+,-,,/,! 等)

 错误的例子:select * from test where id-1=9;
 正确的例子:select * from test where id=10;

3. 使用内部函数导致索引失效.
对于这样情况应当创建基于函数的索引.

// 错误的例子:
select * from test where round(id)=10; //说明,此时id的索引已经不起作用了
 //正确的例子:首先建立函数索引
create index test_id_fbi_idx on test(round(id));
//然后 
select * from test where round(id)=10; 

4. 不要将空的变量值直接与比较运算符(符号)比较。
如果变量可能为空,应使用 IS NULL 或 IS NOT NULL 进行比较,或者使用 ISNULL 函数。

5. 不要在 SQL 代码中使用双引号。
因为字符常量使用单引号。如果没有必要限定对象名称,可以使用(非 ANSI SQL 标准)括号将名称括起来。

6. 以下使用会使索引失效,应避免使用

a. 使用 <> 、not in 、not exist、!=
b. like "%_" 百分号在前(可采用在建立索引时用reverse(columnName)这种方法处理)
c. 单独引用复合索引里非第一位置的索引列.应总是使用索引的第一个列,如果索引是建立在多个列上, 只有在它的第一个列被where子句引用时,优化器才会选择使用该索引。
d. 字符型字段为数字时在where条件里不添加引号.
e. 当变量采用的是times变量,而表的字段采用的是date变量时.或相反情况。

暂时统计到这么多,如果有更多的以后再补充。


MySQL的EXPLAIN的使用

EXPLAIN是用来分析SQL执行情况分析的

EXPLAIN 命令的输出内容大致如下:

mysql> explain select * from user_info where id = 2\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: user_info
   partitions: NULL
         type: const
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 8
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

各列的含义如下:

  • id: SELECT 查询的标识符. 每个 SELECT 都会自动分配一个唯一的标识符.
  • select_type: SELECT 查询的类型.
  • table: 查询的是哪个表
  • partitions: 匹配的分区
  • type: join 类型
  • possible_keys: 此次查询中可能选用的索引
  • key: 此次查询中确切使用到的索引.
  • ref: 哪个字段或常数与 key 一起被使用
  • rows: 显示此查询一共扫描了多少行. 这个是一个估计值.
  • filtered: 表示此查询条件所过滤的数据的百分比
  • extra: 额外的信息

更详细的可以参考这篇【性能优化神器 Explain 使用分析】或者【高性能MySQL


总结

查询优化目的就是为了快速得到结果,所以每当写完SQL应该思考以下几点:

  • 是否需要全表查询以及返回的数据是否合理。
  • 是否需要索引,索引是否合理。
  • 是否有更好的解决办法。


链接: https://fly63.com/article/detial/2992

js中for循环优化总结_如何提高程序的执行效率

在程序开发中,经常会使用到for循环的,但是很多人写的for循环效率都是比较低的,下面就举例说明,并总结优化for循环的方法,来提高我们程序的执行效率。

网站打开速度优化_如何提高网页访问速度技巧方法总结

网站的加载速度不仅影响着用户体验,也会影响搜索引擎的排名,在百度推出“闪电算法”以来,将网站首屏打开速度被列入优化排名行列,作为前端开发的我们需要如果来优化网站的打开速度呢?下面就整理挖掘出很多细节上可以提升性能的东西分享给大家

JS性能优化之文档片段createDocumentFragment

DocumentFragments是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素所代替。因为文档片段存在于内存中,并不在DOM树中

深入浅出代码优化﹣if/else

对于代码裡面的 if else,我们可以使用逻辑判断式,或更好的三元判断式来优化代码。除了可以降低维护项目的成本之外,还可以提升代码可读性。就让我们从最简单的 if else 例子开始吧。

微信小程序性能优化入门指南

小程序从发布到现在也已经有将近两年的时间,越来越来多的公司开始重视小程序生态带来的流量,今年也由于小程序平台对外能力的越来越多的开放以及小程序平台的自身优化,越来越多的开发者也自主的投入到小程序的开发当中

网络串流播放_HTML5如何优化视频文件以便在网络上更快地串流播放

无论你正在将 GIF 动图转换为 MP4 视频,还是手头已经有一大堆 MP4 视频,你都可以优化文件结构,以使得这些视频更快地加载和播放。通过重组 atoms 将 moov 放到文件开头,浏览器可以避免发送额外的 HTTP range request 请求来搜寻和定位 moovatom

​web项目优化_Web 服务器性能与站点访问性能优化

要优化 Web 服务器的性能,我们先来看看 Web 服务器在 web 页面处理上的步骤:Web 浏览器向一个特定的服务器发出 Web 页面请求; Web 服务器接收到 web 页面请求后,寻找所请求的 web 页面,并将所请求的 Web 页面传送给 Web 浏览器; 显示出来

前端性能优化之重排和重绘

浏览器下载完页面所有的资源后,就要开始构建DOM树,于此同时还会构建渲染树(Render Tree)。(其实在构建渲染树之前,和DOM树同期会构建Style Tree。DOM树与Style Tree合并为渲染树)

微信小程序代码优化总汇

写篇文章的目的,是以开放小程序代码的层面的优化。包括:条件判断将wx:if换成了hidden 、页面跳转请销毁之前使用的资源、列表的局部更新、小程序中多张图片懒加载方案、Input状态下隐藏input,应预留出键盘收起的时间

我是如何将页面加载时间从6S降到2S的?

生活在信息爆炸的今天,我们每天不得不面对和过滤海量的信息--无疑是焦躁和浮动的,这就意味着用户对你站点投入的时间可能是及其吝啬的(当然有一些刚需站点除外)。如何给用户提供迅速的响应就显得十分重要了

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!