在确定查询只是返回需要的数据之后,接下来应该看看查询为了返回结果是否扫描了过多的数据。对于MySQL,最简单的衡量查询开销的三个指标如下:

  1响应时间,2扫描行数,3返回行数

  没有那个指明能够完美的衡量查询的开销,但他们大致反映了MySQL在内存执行查询时需要访问多少数据,并可以大概推算出查询运行的时间。这三个指标都会记录到MySQL的慢日志中,索引检查慢日志记录时找出扫描行过多的查询的好办法。

  1响应时间

  要记住,响应时间只是一个表面上的值。这样说肯能看起来和前面关于响应时间的说法有矛盾?其实并不矛盾,响应时间仍然是最重要的指标。这一点有些复杂,细细道来。

  响应时间是两个部分之和,服务时间和排队时间。服务时间是指数据库处理这个查询真正花了多长时间。排队时间是指服务器为了等等某些资源而没有真正执行查询的时间--可能是等待io的完成,也可能是等待锁,等等。遗憾的是,我们无法把响应时间细分到这些部分,除非有什么办法能够逐个测量上面这些消耗,不过很难做到。一般最常见和重要的是等待io和锁等待,但实际情更加复杂。

  所以在不同类型的应用压力下,响应时间并没有什么一致的规律或者公式。诸如存储引擎的锁(表锁,行锁),高并发资源竞争,硬件响应等诸多因素都会影响响应时间,所以,响应时间既可能是一个问题的结果也可能是一个问题的原因,不同案例情况不同。

  当你看到一个查询的响应时间的时候,首先需要问问自己,这个响应时间是否是一个合理的值。实际上可以使用“快速上限估计”法来gust查询的响应时间。概况的说,了解这个查询需要那些索引以及它的执行计划是什么,然后计算大概需要多少个顺序和随机io,在用其诚意在具体硬件条件下的一侧iodine消耗时间,最后把这些消耗都加起来,就可以获得一个大概参考值来排队当前响应时间是不是一个合理的值。

  2.扫描的行数和返回的行数。

  分析查询时,查看该查询扫描的行数是非常有帮助的。这在一定程度上能够说明该查询找到需要的数据的效率高不高。

  对于找出那些糟糕查询,这个指标可能还不够完美,因为并不是所有的行的访问代价都是相同的。较短的行的访问速度相当快,内存中的行业比磁盘中的行的访问速度要快的多。

  理想的情况下,扫描的行数和返回的行数应该是相同的。但实际上这种美事并不多。例如在做一个关联查询的时候,扫描的行数和对返回的行数的比率通常都很小,一般在1:1和10:1之间,不过有时候这个值也可能非常大。

  3扫描的行数和访问类型。

  在评估查询开销的时候,需要考虑一下从表中找到某一行数据的成本。MySQL有好几种访问方式可以查找并返回一行接口。这些访问方式可能需要访问很多行才能返回一条结果,也有些访问方式可能无需扫描就能返回结果。

  在EXPLAIN语句中的type列反映了访问类型。访问类型有很多种,从全表扫描到索引扫描,范围扫描,唯一索引,常数索引。这里列的这些,速度是从慢到快,扫描的行数也是从多到少。你不需要记住这些访问类型,但需要明明扫描表,扫描索引,范围访问和单值访问的概念。

  如果查询没有办法找到合适的访问类型,那么解决的最好办法通常就是增加一个合适的索引,这也是我们前一章讨论的问题。现在应该明白为什么索引对于查询优化如此重要了。索引让MySQL以最高效,扫描行数最少的方式找到需要的记录。

  SELECT * FROM actor WHERE film_id = 1;

  这个查询将返回10 行数据,从explain的结果可以看出,MySQL在索引idx_fk_film_id上使用了ref访问类型来执行查询。

  EXPLAIN SELECT * FROM film_actor WHERE film_id = 1\G

  id:1
  select type :simple

  type:ref

  table :film_actor

  possible key :****

  key :****

  key len:2

  ref :const

  rows:10

  explain的结果也显示MySQL预估需要访问10行数据。换句话说,查询优化器认为这种访问类型可以高效的完成查询。如果没有合适的索引会怎样呢》、?MySQL就不得不使用一种更糟糕的访问类型,下面我们来看看如果删除对应的索引来运行这个查询:

  ALTER TABLE film_actor DROP FOREIGN KEY fk_fiml_actor_film;

  ALTER TABLE film_actor DROP KEY idx_fk_film_id

  EXPLAIN SELECT * FROM film_actor WHERE film_id=1 \G

  id:1
  select_type:simple

  type:ALL

  table:film_actor

  possible_key:NULL

  kye:NULL;

  key_len:null

  ref :null

  rows:5073

  EXTRA :using where

  

  正如我们预测的,访问类型变成了一个全表扫描,限制MySQL预估需要扫描5073条记录就能完成这个查询。这里的USING WHERE 表示MySQL将通过WHERE 条件来筛选存取引擎返回的记录。

  一般MySQL能够使用如下三种方式使用WHERE 条件,从好到坏依次为:

  1在索引中使用where调价来过滤掉不匹配的记录。这是在存储引擎层完成的。

  2使用索引覆盖扫描(在Extra列中出现USING INDEX )来返回记录,直接从索引中过滤掉不需要的记录并返回命中结果。这是在MySQL服务器层完成的,但无需再回表查询记录。

  3从数据表中返回数据,然后过滤不满足条件的记录(在extra 列中出现using where)。这是在MySQL服务器层完成的,MySQL需要从数据表中读出记录然后过滤。上面这个例子说明了好的索引是多么重要。好的索引可以让查询使用合适的访问类型,尽可能的只扫描需要的数据行。但也不是说增加索引就能让扫描的行数等于返回的行数。例如下面的使用聚合函数COUNT()的查询:

  SELECT actor_id ,COUNT(*) FROM film_actor GROUP BY actor_id

  这个查询需要读取几千行数据,但是仅返回了200行结果。没有什么索引能够让这样的查询减少需要扫描的行数。

  不幸的是,MySQL不会告诉我们生成结果实际上需要扫描多数行数据,而只会告诉我们生成结果时一共扫描了多数行数据。扫描的函数中大部分都很可能被where条件过滤掉的,对最终的结果并没有贡献。在上面的例子中,我们傻叉收益后,看到MySQL需要扫描索引记录然后根据where条件过滤,最终返回10行结果。理解一个查询需要扫描多数行和实际需要使用的行数首先需要理解这个查询背后的逻辑和思想。

  如果发现查询需要大量的数据但值返回少数行,那么通常可以尝试下面的技巧去优化它:

  1使用索引覆盖扫描,把所有需要用的列都放到索引中,这样存储引擎无需回表获取对应的行就可以返回结果了

  2该表库表结构。例如使用单独的汇总表、

  3重写这个查询,然MySQL优化器能够以更优化的方式执行这个查询。

MySQL是否在扫描额外的记录的更多相关文章

  1. MYSQl 全表扫描以及查询性能

    MYSQl 全表扫描以及查询性能 -- 本文章仅用于学习,记录 一. Mysql在一些情况下全表检索比索引查询更快: 1.表格数据很少,使用全表检索会比使用索引检索更快.一般当表格总数据小于10行并且 ...

  2. mysql插入数据与删除重复记录的几个例子(收藏)

    mysql插入数据与删除重复记录的几个例子 12-26shell脚本实现mysql数据的批量插入 12-26mysql循环语句插入数据的例子 12-26mysql批量插入数据(insert into ...

  3. MySQL查询数据表中数据记录(包括多表查询)

    MySQL查询数据表中数据记录(包括多表查询) 在MySQL中创建数据库的目的是为了使用其中的数据. 使用select查询语句可以从数据库中把数据查询出来. select语句的语法格式如下: sele ...

  4. mysql中根据一个字段相同记录写递增序号,如序号结果,如何实现?

      mysql中根据一个字段相同记录写递增序号,如序号结果,如何实现? mysql中实现方式如下: select merchantId, NameCn, send_date, deliver_name ...

  5. Web开发入门经典:使用PHP6、Apache和MySQL 中文pdf扫描版​

    通过学习本书,读者很快就能明白为什么PHP.Apache和MySQL会迅速成为开发动态网站最流行的方式,本书将为读者理解这3个核心组件如何独立工作和协同工作奠定良好的基础,引导读者充分利用它们提供的各 ...

  6. php MySQL使用rand函数随机取记录(转)

    php MySQL使用rand函数随机取记录 如何在mysql中使用随机数, 如何写一个语句能一下更新几百条MYSQL数据! 需要测试MYSQL数据库,里面有一个上万条数据的数据库,如何写一个PHP文 ...

  7. MySQL使用INSERT插入多条记录

    MySQL使用INSERT插入多条记录,应该如何操作呢?下面就为您详细介绍MySQL使用INSERT插入多条记录的实现方法,供您参考. 看到这个标题也许大家会问,这有什么好说的,调用多次INSERT语 ...

  8. 12 | 为什么我的MySQL会“抖”一下? 学习记录

    <MySQL实战45讲>12 | 为什么我的MySQL会“抖”一下? 学习记录 http://naotu.baidu.com/file/15aa54cab2fa882c6a2a1dd52e ...

  9. MySQL按时间统计每个小时记录数

    MySQL按时间统计每个小时记录数 方案1: ? 1 2 3 4 5 6 7 SELECT  @rownum := @rownum + 1 AS ID,         CONCAT((CASE WH ...

随机推荐

  1. Cannot find module formidable

    今天按照例子学习安装 formidable 即使我-g全局安装,也不行.. 于是搜到了这句话.. 解决了 我也遇到这个问题,困扰了我一天,现在找到解决办法了.我在看这篇入门教程:http://node ...

  2. ubuntu 下安装nginx

    y@y:~$ sudo apt-get install nginx y@y:~$ sudo service nginx start y@y:~$ nginx默认使用80端口,打开浏览器输入:http: ...

  3. SDUT2087 离散事件模拟-银行管理(模拟)

    题目链接. 分析: 模拟. 果然模拟什么的最讨厌了. 用e1,e2分别记录队列1,队列2的结束时间. 每个结点的s记录开始时间,e一开是记录逗留时间,进队列的时候,改成离开的时间.时刻记录总时间就可以 ...

  4. 【转】linux内核调试技巧之一 dump_stack

    原文网址:http://blog.csdn.net/dragon101788/article/details/9419175 在内核中代码调用过程难以跟踪,上下文关系复杂,确实让人头痛 调用dump_ ...

  5. 《Ruby语言入门教程v1.0》学习笔记-02

    9.18 第四章 一切都是对象 这个章节的例子都举得很浅显易懂,而且作者的语言= =噗,委实生动有趣啊是~~ 4.1  两种思维方式 初期的编程思想是:以“如何做”为指导来编写代码.这时期的编程语言叫 ...

  6. app开发历程————Android程序解析服务器端的JSON格式数据,显示在界面上

    上一篇文章写的是服务器端利用Servlet 返回JSON字符串,本文主要是利用android客户端访问服务器端链接,解析JSON格式数据,放到相应的位置上. 首先,android程序的布局文件main ...

  7. 解决Chrome无法加载Shockwave Flash

    Shockwave Flash 是 Adobe Flash Player下的一个小插件,你可以在Google商店中找到并下载. 通常来讲,Shckwave Flash会在安装Flash Player的 ...

  8. Mysql 的字符编码机制、中文乱码问题及解决方案【转载】

    本文转载自:http://hi.baidu.com/huabinyin/item/7f51e462df565c97c4d24929.感谢作者及相关博主.        相信很多朋友都会对字符编码敬而远 ...

  9. Struts2初学习记录

    以下笔记内容来自尚硅谷_Struts2_佟刚老师的视频教程+自己一点点整理 来源免责声明 一. 1. VS 自实现: 1). 搭建 Struts2 的开发环境 2). 不需要显式的定义 Filter, ...

  10. HTTP学习笔记1-基本定义

    1,HTTP(Hypertext  Transfer  Protocol)超文本传输协议,http是TCP/IP协议的一个应用层协议:从1990年开始就在www上广泛应用,是现今在www上应用的最多的 ...