热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

mysql高性能查询_读薄《高性能MySql》(四)查询性能优化

对MySql进行优化,必须对Scheme,索引,查询语句一同优化。通过前面的章节我们掌握了Scheme和索引的优化,最后我们

对 MySql 进行优化,必须对 Scheme,索引,查询语句一同优化。

通过前面的章节我们掌握了 Scheme 和 索引的优化,最后我们来看一下查询优化。

为了优化查询,我们必须先了解查询是怎样执行的,然后探讨优化器在哪些方面做得还不足,以帮助 MySql 更有效的执行查询。

优化数据访问

在一条 Sql 语句执行的很慢的时候,可以从以下两个方面来分析:

是否在检索的时候访问了太多的行或者列

MySql 服务器是否在分析大量超过需要的行

请求了不需要的数据

万恶之源 SELECT *

一个很好用的观点就是在每次使用 SELECT * 取出全部行的时候都要审视一下自己是否需要全部数据。

取出所有列可能使得索引覆盖无效,一些 DBA 是严格禁止 SELECT * 的写法的。

重复查询数据

有些地方可能会不小心的重复查询了相同的数据。比如在论坛中,如果一个人回复多次,很有可能会一不小心每次都去请求这个人的资料,一个有效的方法就是使用缓存。

扫描额外的记录

确定查询只返回需要的数据以后,接下来该看一下为了返回需要的记录是否扫描了太多行了。有两个指标我们需要关注,一个是扫描的行数和返回行数的比值,另外一个是扫描的访问类型。

扫描行数和返回行数的比值如果过低,则需要扫描大量的数据才能返回结果,通常可以使用如下的方法来保存数据:

使用索引覆盖,把所有的列放入索引中,就无需扫描表了

改变数据库结构,比如采用单独的表汇总表

重新写这个 SQL 语句

在 EXPLAIN 语句中的 type 列中反应了访问类型,从慢到快分别是:

全表扫描,索引扫描,范围扫描,唯一索引查询,常数引用。

如果查询没有使用合适的类型,可以合理的添加索引。

重构查询方式

将一个复杂查询拆分成多个查询

MySql 从设计上让连接和断开都很快。如果只是返回一个小小的结果,MySql 非常高效。

当然能一个查询就解决的要尽量写成一个查询,只是告诉大家不要太惧怕把查询拆分开来会带来性能损失。

切分查询

有时候一个大查询会占用表锁很久,影响业务。这时候可以将大查询分为小查询,每次执行这个查询的一小部分。

比如定期清除大量数据的时候,如果有一个大的语句一次性完成,则可能会占住很多资源,影响其他查询。

将删除改写成一次删除一小部分数据,分散开来在不同时间执行,可以将服务器压力分散到很长的一个时间段中。

分解关联查询

很多高性能应用会将一个大的关联查询分解成多个单表查询。

让缓存效率更高,许多应用可以缓存单表查询的结果,那么下次查询的时候可以减少这次查询

单个查询减少 锁的竞争

更容易对数据库进行拆分

减少冗余记录查询

MySql 查询过程

MySQL 通信协议

MySQL 客户端和服务端的通信是半双工的,这意味着同一个时刻内,客户端和服务端只有一方在发送数据。一旦一方开始发送数据,另外一端必须接受完整个消息才能进行响应。

这就是为什么当查询语句特别长的时候,max_allowed_packet 特别重要了。所以在必要的时候需要添加 LIMIT 限制。

查询状态

对于一个 MySQL 连接,任何时刻都有一个状态,该状态表示了 MySQL 当前正在做什么,用 SHOW FULL PROCESSLIST 命令即可。

Sleep

线程正在等待客户端发送新的请求。

Query

线程正在执行查询或者在将结果发送给客户端

Locked

该线程在等待表锁

Analyzing and statistics

线程正在收集存储引擎的统计信息,并且生成执行计划。

Copying to tmp table

线程正在把数据复制到一个临时表中,一般在 Group By 或者排序的时候会出现这个状态。

Sorting result

线程正在排序数据

Sending data

线程可能在多个状态之间传送数据,或者在向客户端返回数据。

MySQL 对关联表顺序优化

MySQL 的优化器会对查询进行静态和动态优化,期中我们只挑最重要的优化讲,也就是对关联表顺序的优化。

我们先来看一个 UNION 的例子,对于 UNION 查询,MySQL 会将单个查询结构放入一个临时表(注意临时表是没有索引的)中,然后再重新读出临时表数据来完成 UNION 查询。

MySQL 关联执行策略很简单,对于任何关联都执行嵌套循环关联操作,即先从一个表读出数据,然后嵌套循环到下一个表中取出匹配的行,依次下去,直到找到所有的表中匹配的行为止。然后根据各个表匹配的行,返回查询中需要的各个列。MySQL 会尝试在最后一个关联表中找到所有匹配的行,如果最后一个关联表无法找到更多的行后,MySQL 返回到上一层次关联表,看是否能找到更多的匹配记录,依次类推迭代查询。

关联查询优化器

MySQL 优化器决定了多个表关联的顺序,关联优化器可以选择一个代价最小的关联顺序。

有时候优化器选择的不是最优的顺序,这时候可以使用 STRAUGHT_JOIN 关键字进行查询,让优化器按照你认为最优的顺序查询,但是一般来说人判断的都没有优化器好。

优化器会尝试在所有的顺序中选择一个成本最小的关联顺序,但是当表非常多的时候,比如有 n 张表进行关联,就要进行 n! 次比较。当表超过 optimizer_search_depth 的时候,就会选择贪婪搜索模式了。

MySQL 查询优化器限制

子查询

MySQL 的子查询优化的相当糟糕,最糟糕的一类是子查询中 WHERE 条件包含了 IN() 的子查询。比如用下面的语句查询

SELECT * FROM film WHERE film.id in (SELECT file_id from film_actor WHERE actor_id = 1)

我们可能会认为 MySQL 会执行后面的语句选择出 id 后才执行前面的查询,但是 MySQL 会将外层查询压入子查询中

SELECT * FORM film WHERE EXISTS(SELECT * FROM film_actor WHERE actor_id = 1 AND film_actor.film.id = film.id)

这个查询会对 film 进行全表扫描,性能非常糟糕。

所以我们最好用联合查询来代替这个查询。

这个问题直到 MySQL 5.5 还存在,MySQL 另外一个分支 MariaDB 在原有的基础上做了大量的改进,例如这里带 IN 的子查询。

当一个查询能被写成子查询和联合查询的时候,最好通过一些测试来判断哪个写法更快一些

UNION

有时候 MySQL 无法将闲置条件由外层推到内层,这使得本能限制扫描行数的 LIMIT 在内层查询中不起作用。

如果希望 UNION 的各个子句能根据 LIMIT 只取出部分结果集,或者希望能先排好序再分别使用这些子句,那么需要分别对这些查询使用 LIMIT 和 ORDER BY。

(SELECT * FROM XXX LIMIT 20) UNION ALL (SELECT * FROM XXX LIMIT 20)

并发执行

MySQL 无法利用多核特性来并发执行查询。

最大值和最小值

对于 MIN 和 MAX 查询,MySQL 的优化做的不是很好,

SELECT MIN(id) FROM actor

因为 id 是递增的,所以只需要扫描一行即可,但是 MySQL 仍然会做全表扫描。可以改下面的写法

SELECT id FROM actor LIMIT 1

特定优化查询

一般来说,使用 Percona Toolkit 中的 pt-query-advisor 能够解析查询日志,分析查询模式,然后给出详细的建议来帮助你优化 SQL 语句。

优化 COUNT 查询

当 COUNT 的值不可能为空的时候,MySQL 会转向统计行数。如果我们想要统计行数的时候,最好直接使用 COUNT(*)。

使用近似值

有时候某些业务不需要精确值,此时可以用近似值来代替,EXPLAIN 出来的优化器估算的行数就是一个不错的近似值,执行 EXPLAIN 不需要去真正的执行查询,效率高很多。

优化关联查询

确保 ON 或者 USING 上的列有索引,在创建索引的时候需要考虑到关联列的顺序,比如说表 A,B 用列 c 进行关联的时候,如果优化器的关联顺序是 B,A,则只需要在 A 上建立索引即可。

确保任何的 GROUP BY 和 ORDER BY 只涉及到一个表中的列

优化子查询

关于子查询给出的最主要的优化方法是:尽量使用关联查询代替子查询,因为 MySQL 的子查询优化的非常烂。不过这条意见只在旧版本有用,在 MySQL 5.6 以上和 MariaDB 中,可以忽略掉这条优化。

优化 GROUP BY 和 DISTINCT

MySQL 经常用同样的方法来优化这两个查询,它们都会用索引来优化,这也是最有效的优化办法。

当无法使用索引的时候,MySQL 会用临时表或者文件排序来执行 GROUP BY。

如果需要对关联查询做分组,那么通常采用标识列来进行分组效率会比较高。

优化 LIMIT 分页

当系统需要进行分页操作的时候通常会使用 LIMIT 加 偏移量的操作,同时加上合适的 ORDER BY 语句。如果有对应的索引,效率通常会不错。

但是当偏移量非常大的时候,LIMIT 10000,20,这种语句会导致扫描了10020 行,但是只返回 20 行。

优化这种查询的方法有:

使用索引覆盖,只搜索索引覆盖的行然后通过一次查询把所有需要的数据查找出来

通过延迟关联,后面会讨论这个方法

优化 SQL_CALC_FOUND_ROWS

分页的时候有时候会通过在 LIMIT 语句中加上 SQL_CALC_FOUND_ROWS。这样就可以获取去掉 LIMIT 条件后查询的行数,加上这个提示以后,不管是否需要,都会把全部的行都扫描一遍,而不是在满足了 LIMIT 的大小后停止扫描,这样会带来很大开销。

解决这个问题有两个方法

采用 EXPLAIN ROW 中的近似值,有时候不需要那么精准的数据

先获得比较多的缓存集,比如设置一个 100 页和一个 100 页以后的按钮,当用户需要 100 页后的按钮再去获取。

优化 UNION 查询

除非确实需要服务器消除重复的行,否则必须要使用 UNION ALL。

如果没有 ALL 关键字,MySQL 会给临时表加上 DISTINCT 选项,然后做一次查重操作,这将带来极大的开销。



推荐阅读
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • MySQL多表数据库操作方法及子查询详解
    本文详细介绍了MySQL数据库的多表操作方法,包括增删改和单表查询,同时还解释了子查询的概念和用法。文章通过示例和步骤说明了如何进行数据的插入、删除和更新操作,以及如何执行单表查询和使用聚合函数进行统计。对于需要对MySQL数据库进行操作的读者来说,本文是一个非常实用的参考资料。 ... [详细]
  • MySQL之left join表查询中发生字符集转换导致表索引失效
    mysql,之,left,join,表,查询,中,发生,字符,集,转换, ... [详细]
  • 本文介绍了如何在MySQL中将零值替换为先前的非零值的方法,包括使用内联查询和更新查询。同时还提供了选择正确值的方法。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 本文介绍了在MySQL8.0中如何查看性能并解析SQL执行顺序。首先介绍了查询性能工具的开启方法,然后详细解析了SQL执行顺序中的每个步骤,包括from、on、join、where、group by、having、select distinct、union、order by和limit。同时还介绍了虚拟表的概念和生成过程。通过本文的解析,读者可以更好地理解MySQL8.0中的性能查看和SQL执行顺序。 ... [详细]
  • VueCLI多页分目录打包的步骤记录
    本文介绍了使用VueCLI进行多页分目录打包的步骤,包括页面目录结构、安装依赖、获取Vue CLI需要的多页对象等内容。同时还提供了自定义不同模块页面标题的方法。 ... [详细]
  • MFC程序连接MySQL成功实现查询功能,但无法实现修改操作——详解查询语句在MySQL中的使用过程
    selectxxx,xxx,xxxfromxxxwherexxxxxx,xxxxxx程序的日常开发中,我们经常会写到各种各样的简单的,复杂的查询sql语 ... [详细]
  • Linux环境中使用Mysql数据库
    Linux下Mysql数据库MySQL是一个开放源码的小型关联式数据库管理系统,开发者为瑞典MySQLAB公司,目前属于Oracle公司,MySQL被广泛地应用在I ... [详细]
  • Centos7安装MySql5.6
    如何在CentO ... [详细]
  • 阿里云主机实战应用之centos7上的防火墙设置
    最近公司又上了一台服务器,以前都是用centos6系统,这次选择使用了centos7系统的安装镜像,因为现在程序版本在centos7上一般php默认就是5.4以上的,mysql也 ... [详细]
  • centos7如何利用yum安装mysql 8.0.12
    这篇文章将为大家详细讲解有关centos7如何利用yum安装mysql8.0.12,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了CentOS7编译mysql8.0.12相关的知识,希望对你有一定的参考价值。步骤一:安装 ... [详细]
  • MySQL/MariaDB/PerconaDB提权条件漏洞
    背景  2016年11月01日,国外安全研究员DawidGolunski在MyS ... [详细]
author-avatar
顾久君_152_599
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有