热门标签 | 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 选项,然后做一次查重操作,这将带来极大的开销。



推荐阅读
  • Hadoop的文件操作位于包org.apache.hadoop.fs里面,能够进行新建、删除、修改等操作。比较重要的几个类:(1)Configurati ... [详细]
  • 基于iSCSI的SQL Server 2012群集测试(一)SQL群集安装
    一、测试需求介绍与准备公司计划服务器迁移过程计划同时上线SQLServer2012,引入SQLServer2012群集提高高可用性,需要对SQLServ ... [详细]
  • 本文详细介绍了在 CentOS 7 系统中配置 fstab 文件以实现开机自动挂载 NFS 共享目录的方法,并解决了常见的配置失败问题。 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
  • 如何在Linux服务器上配置MySQL和Tomcat的开机自动启动
    在Linux服务器上部署Web项目时,通常需要确保MySQL和Tomcat服务能够随系统启动而自动运行。本文将详细介绍如何在Linux环境中配置MySQL和Tomcat的开机自启动,以确保服务的稳定性和可靠性。通过合理的配置,可以有效避免因服务未启动而导致的项目故障。 ... [详细]
  • 在软件开发过程中,经常需要将多个项目或模块进行集成和调试,尤其是当项目依赖于第三方开源库(如Cordova、CocoaPods)时。本文介绍了如何在Xcode中高效地进行多项目联合调试,分享了一些实用的技巧和最佳实践,帮助开发者解决常见的调试难题,提高开发效率。 ... [详细]
  • 本文节选自《NLTK基础教程——用NLTK和Python库构建机器学习应用》一书的第1章第1.2节,作者Nitin Hardeniya。本文将带领读者快速了解Python的基础知识,为后续的机器学习应用打下坚实的基础。 ... [详细]
  • 本文详细介绍了如何在 Linux 系统上安装 JDK 1.8、MySQL 和 Redis,并提供了相应的环境配置和验证步骤。 ... [详细]
  • 在 Ubuntu 中遇到 Samba 服务器故障时,尝试卸载并重新安装 Samba 发现配置文件未重新生成。本文介绍了解决该问题的方法。 ... [详细]
  • 使用虚拟机配置服务器
    本文详细介绍了如何使用虚拟机配置服务器,包括购买云服务器的操作步骤、系统默认配置以及相关注意事项。通过这些步骤,您可以高效地配置和管理您的服务器。 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 本文详细介绍了 InfluxDB、collectd 和 Grafana 的安装与配置流程。首先,按照启动顺序依次安装并配置 InfluxDB、collectd 和 Grafana。InfluxDB 作为时序数据库,用于存储时间序列数据;collectd 负责数据的采集与传输;Grafana 则用于数据的可视化展示。文中提供了 collectd 的官方文档链接,便于用户参考和进一步了解其配置选项。通过本指南,读者可以轻松搭建一个高效的数据监控系统。 ... [详细]
  • 本文详细介绍了在MySQL中如何高效利用EXPLAIN命令进行查询优化。通过实例解析和步骤说明,文章旨在帮助读者深入理解EXPLAIN命令的工作原理及其在性能调优中的应用,内容通俗易懂且结构清晰,适合各水平的数据库管理员和技术人员参考学习。 ... [详细]
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社区 版权所有