这个是我在jobbole.com 上看到的

先给出数据表table结构

mysql> show create table tt \G
*************************** 1. row ***************************
Table: tt
Create Table: CREATE TABLE `tt` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` char(16) NOT NULL DEFAULT '',
`rule_id` int(10) unsigned NOT NULL DEFAULT '0',
`status` tinyint(3) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
KEY `ttx` (`name`,`rule_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1176504 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

目前数据库中数据大概有2000W条,之后可能还会不断增长,现在想要查询的是:从表中找出相同姓名,相同规则ID,并且status = 1对应的最后一条记录

写个程序随机加入数据

<?php

mysql_connect('10.0.0.234','root','123456');
mysql_select_db('testdb');
mysql_query("set names utf8"); $strs ='';
$start = 'insert into tt values ';
for($i=0;$i<3000000;$i++){ $k = range(a,z);
shuffle($k);
$str = implode('',$k);
$num = mt_rand(8,16);
$in = substr($str,0,$num); $strs .= " (NULL,'$in',$num,1),";
if($i%10000 == 0){
echo $i."\n";
$sql = trim($start.$strs,',');
mysql_query($sql);
$strs ='';
}
}

这样300W数据就有了

目前的查询语句是这样的

SELECT * FROM tt WHERE id IN (
SELECT max(id) AS max_id FROM tt GROUP BY name, rule_id)
AND status = 1 

看一下查询计划

mysql> explain SELECT * FROM tt WHERE id IN ( SELECT max(id) AS max_id FROM tt GROUP BY name, rule_id) and status=1;
+----+--------------------+-------+-------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+-------+---------------+------+---------+------+---------+-------------+
| 1 | PRIMARY | tt | ALL | NULL | NULL | NULL | NULL | 1176818 | Using where |
| 2 | DEPENDENT SUBQUERY | tt | index | NULL | ttx | 52 | NULL | 1 | Using index |
+----+--------------------+-------+-------+---------------+------+---------+------+---------+-------------+

索引用上了,也没有filesort,这是不是就很快了,然后执行查询,就卡主了,卡主了,最后只能被Ctrl+C了

那么这个不行,就换一个,这里我想到了,子查询换成join看一看效果

SELECT a.id,b.name,b.rule_id  FROM (select max(id) as id from tt   group by name, rule_id) as a left join tt b on a.id = b.id

看一下查询计划

mysql> explain SELECT a.id,b.name,b.rule_id  FROM (select max(id) as id from tt   group by name, rule_id) as a left join tt b on a.id = b.id;
+----+-------------+------------+--------+---------------+---------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+---------------+---------+---------+------+---------+-------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 1176503 | |
| 1 | PRIMARY | b | eq_ref | PRIMARY | PRIMARY | 4 | a.id | 1 | |
| 2 | DERIVED | tt | index | NULL | ttx | 52 | NULL | 1176818 | Using index |
+----+-------------+------------+--------+---------------+---------+---------+------+---------+-------------+

执行结果  1.77579775 sec

mysql> show profiles;
+----------+------------+-------------------------------------------------------------------------------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-------------------------------------------------------------------------------------------------------------------------------+
| 6 | 1.77579775 | SELECT a.id,b.name,b.rule_id FROM (select max(id) as id from tt group by name, rule_id) as a left join tt b on a.id = b.id |
+----------+------------+-------------------------------------------------------------------------------------------------------------------------------+
7 rows in set (0.00 sec)

看一看执行 过程

mysql> show profile for query  6;
+------------------------------+----------+
| Status | Duration |
+------------------------------+----------+
| Sending data | 0.000596 |
| Waiting for query cache lock | 0.000002 |
| Sending data | 0.000579 |
| Waiting for query cache lock | 0.000002 |
| Sending data | 0.000534 |
| Waiting for query cache lock | 0.000002 |
| Sending data | 1.101490 |
| end | 0.000010 |
| query end | 0.000004 |
| closing tables | 0.000003 |
| removing tmp table | 0.001369 |
| closing tables | 0.000010 |
| freeing items | 0.000024 |
| logging slow query | 0.000002 |
| logging slow query | 0.000035 |
| cleaning up | 0.000003 |
+------------------------------+----------+
100 rows in set (0.00 sec)

太多的查询缓存lock,关掉它,再看一下查询结果

mysql> show profile for query  10;
+---------------------------+----------+
| Status | Duration |
+---------------------------+----------+
| starting | 0.000154 |
| checking permissions | 0.000006 |
| checking permissions | 0.000005 |
| Opening tables | 0.000124 |
| System lock | 0.000122 |
| optimizing | 0.000007 |
| statistics | 0.000035 |
| preparing | 0.000018 |
| executing | 0.000012 |
| Sorting result | 0.000006 |
| Sending data | 0.518897 |
| converting HEAP to MyISAM | 0.070147 |
| Sending data | 0.067123 |
| init | 0.000033 |
| optimizing | 0.000003 |
| statistics | 0.000020 |
| preparing | 0.000009 |
| executing | 0.000001 |
| Sending data | 1.193679 |
| end | 0.000011 |
| query end | 0.000010 |
| closing tables | 0.000002 |
| removing tmp table | 0.001491 |
| closing tables | 0.000011 |
| freeing items | 0.000020 |
| logging slow query | 0.000002 |
| logging slow query | 0.000050 |
| cleaning up | 0.000003 |
+---------------------------+----------+

再次分析,我们发现 converting HEAP to MyISAM 这个很耗时

mysql> select @@max_heap_table_size/1024/1024;
+---------------------------------+
| @@max_heap_table_size/1024/1024 |
+---------------------------------+
| 16.00000000 |
+---------------------------------+ mysql> set max_heap_table_size = 16777216*4;
Query OK, 0 rows affected (0.00 sec) mysql> select @@max_heap_table_size/1024/1024;
+---------------------------------+
| @@max_heap_table_size/1024/1024 |
+---------------------------------+
| 64.00000000 |
+---------------------------------+

再看看执行结果  1.77579775 > 1.68962725 ,还是有效果的

mysql> show profiles;
+----------+------------+-------------------------------------------------------------------------------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-------------------------------------------------------------------------------------------------------------------------------+
| 17 | 1.68962725 | SELECT a.id,b.name,b.rule_id FROM (select max(id) as id from tt group by name, rule_id) as a left join tt b on a.id = b.id |
+----------+------------+-------------------------------------------------------------------------------------------------------------------------------+

分析

mysql> show profile for query  17;
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 0.000120 |
| checking permissions | 0.000004 |
| checking permissions | 0.000003 |
| Opening tables | 0.000106 |
| System lock | 0.000113 |
| optimizing | 0.000007 |
| statistics | 0.000044 |
| preparing | 0.000011 |
| executing | 0.000006 |
| Sorting result | 0.000002 |
| Sending data | 0.567858 |
| init | 0.000032 |
| optimizing | 0.000004 |
| statistics | 0.000017 |
| preparing | 0.000015 |
| executing | 0.000002 |
| Sending data | 1.120159 |
| end | 0.000011 |
| query end | 0.000005 |
| closing tables | 0.000002 |
| removing tmp table | 0.001020 |
| closing tables | 0.000011 |
| freeing items | 0.000018 |
| logging slow query | 0.000002 |
| logging slow query | 0.000056 |
| cleaning up | 0.000004 |
+----------------------+----------+

好看多了,耗时的地方都在 Sending data 上了,如果硬盘换成PCI-SSD 估计又能提高不少

还有没有其他方法呢,当然有,那么换一种写法

先看查询计划

mysql> explain select max(concat_ws(' ',lpad(id,5,' '),status)) as res, name, rule_id from tt group by name, rule_id ;
+----+-------------+-------+-------+---------------+------+---------+------+---------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+---------+-------+
| 1 | SIMPLE | tt | index | NULL | ttx | 52 | NULL | 1176818 | |
+----+-------------+-------+-------+---------------+------+---------+------+---------+-------+

 再看看执行结果

mysql> show profiles;
+----------+------------+-------------------------------------------------------------------------------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-------------------------------------------------------------------------------------------------------------------------------+
| 22 | 1.82505025 | select max(concat_ws(' ',lpad(id,5,' '),status)) as res, name, rule_id from tt group by name, rule_id |
+----------+------------+-------------------------------------------------------------------------------------------------------------------------------+

好像更慢了 1.82505025 > 1.68962725

分析一下

mysql> show profile for query  22;
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 0.000157 |
| checking permissions | 0.000007 |
| Opening tables | 0.000019 |
| System lock | 0.000020 |
| init | 0.000032 |
| optimizing | 0.000005 |
| statistics | 0.000016 |
| preparing | 0.000012 |
| executing | 0.000008 |
| Sorting result | 0.000003 |
| Sending data | 1.824677 |
| end | 0.000012 |
| query end | 0.000005 |
| closing tables | 0.000009 |
| freeing items | 0.000016 |
| logging slow query | 0.000002 |
| logging slow query | 0.000049 |
| cleaning up | 0.000004 |
+----------------------+----------+

时间基本上都花费在send data了

这里说一下,max + group by 完全就是一个坑,如果有多个字段要返回数据不能这样写

select  max(id) ,type ,name from table  where type=xx group by name

因为group by默认返回第一条记录

如果像下面那样写,肯定会遇到坑的

select max(id) as res, name, rule_id from tt group by name, rule_id ;

这样也可以,而且快多了,但是如果有其他字段怎么办

这样的语句基本上没有什么可以再优化了,只能换换其他方式了,比如:换SSD+raid10 ,分区/分表/分库+中间件了

【mysql】一条慢查询sql的的分析的更多相关文章

  1. 在MySQL中使用explain查询SQL的执行计划

    1.什么是MySQL执行计划 要对执行计划有个比较好的理解,需要先对MySQL的基础结构及查询基本原理有简单的了解. MySQL本身的功能架构分为三个部分,分别是 应用层.逻辑层.物理层,不只是MyS ...

  2. 如何在MySQL中使用explain查询SQL的执行计划?

    1.什么是MySQL执行计划 要对执行计划有个比较好的理解,需要先对MySQL的基础结构及查询基本原理有简单的了解. MySQL本身的功能架构分为三个部分,分别是 应用层.逻辑层.物理层,不只是MyS ...

  3. MySQL字符集不一致导致查询SQL性能问题

    今天做了一个MySQL数据库中的SQL优化. 结论是关联字段字符集不同,导致索引不可用. 查询的SQL如下: select `Alias`.`Grade`, `Alias`.`id`, `Alias` ...

  4. 转: 从Mysql某一表中随机读取n条数据的SQL查询语句

    若要在i ≤ R ≤ j 这个范围得到一个随机整数R ,需要用到表达式 FLOOR(i + RAND() * (j – i + 1)).例如, 若要在7 到 12 的范围(包括7和12)内得到一个随机 ...

  5. 从Mysql某一表中随机读取n条数据的SQL查询语句

    若要在i ≤ R ≤ j 这个范围得到一个随机整数R ,需要用到表达式 FLOOR(i + RAND() * (j – i + 1)).例如, 若要在7 到 12 的范围(包括7和12)内得到一个随机 ...

  6. MySql 学习之 一条查询sql的执行过程

    相信大家都接触过Mysql数据库,而且也肯定都会写sql.我不知道大家有没有这样的感受,反正我是有过这样的想法.就是当我把一条sql语句写完了,并且执行完得到想要的结果.这时我就在想为什么我写这样的一 ...

  7. mysql系列-⼀条SQL查询语句是如何执⾏的?

    ⼀条SQL查询语句是如何执⾏的? ⼤体来说,MySQL 可以分为 Server 层和存储引擎层两部分 Server 层 Server 层包括连接器.查询缓存.分析器.优化器.执⾏器等,涵盖 MySQL ...

  8. 不同数据库,查询前n条数据的SQL语句

    不同的数据库,支持的SQL语法略有不同,以下是不同数据库查询前n条数据的SQl语句 SQL Server(MSSQL) SELECT TOP n * FROM table_name ORACLE SE ...

  9. 提高MYSQL百万条数据的查询速度

    提高MYSQL百万条数据的查询速度 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 nul ...

随机推荐

  1. EF6源码学习-准备篇

    现在对于.net开发人员来说EF已经很流行了,虽然我在2010年的时候就用过EF,也看过几本书,但是还没有仔细研究EF的code, 曾经也尝试阅读EF5的源代码,后来由于时间关系也没有坚持住.现在计划 ...

  2. 【图片识别】Java中使用tess4J进行图片文字识别(支持中文)(转)

    http://blog.csdn.net/wsk1103/article/details/54173282 java中识别文字比较简单,使用的软件是tesseractocr(使用的版本是3.02,3以 ...

  3. [Canvas]首个小游戏告成

    英雄在地图上射箭杀怪兽,杀完了就胜利了. 点此下载程序试玩. 图例: 代码: <!DOCTYPE html> <html lang="utf-8"> < ...

  4. IntelliJ IDEA 优化总结

    1.修改JVM参数 (IntelliJ IDEA 10.0.1包含以上版本不需要设置) 修改idea.exe.vmoptions配置文件调整以下内容:-Xms256m-Xmx384m-XX:MaxPe ...

  5. git 本地与服务器的同步

    如果在服务器上直接用 git init 则会自动在当前目录创建.git目录但在本地clone后修改后,不能push,这是由于git默认拒绝了push操作,需要进行设置,修改.git/config添加如 ...

  6. 详解使用DockerHub官方的mysql镜像生成容器

    详解使用DockerHub官方的mysql镜像生成容器 收藏 yope 发表于 10个月前 阅读 1506 收藏 32 点赞 1 评论 0 腾讯云·云上实验室:开发者零门槛,免费使用真机在线实验!&g ...

  7. android中使用toolbar

    系统默认使用的是ActionBar,就是界面中的标题栏,但是由于ActionBar设计的原因,被限定只能位于活动的顶部,从而不能实现Material Design效果,所以官方建议使用Toolbar替 ...

  8. NSString json 车NSDictionary

    NSData *jsonContent = [[userInfo objectForKey:@"acme"] dataUsingEncoding:NSUTF8StringEncod ...

  9. asp.net时间类-格式-方法应用

    一.当前日期+时间DateTime.Now c#/asp.net通过DateTime.Now这个类来获取当前的时间. DateTime dt = DateTime.Now; 2013/10/24 10 ...

  10. ubuntu下配置华为交换机s2016

    因为笔记本没有RS232口,只有装有ubuntu的服务器上有RS232口,于是登录ubuntu 配置连接. 这里使用minicom,当然,Ubuntu源还有个图形界面的gtkterm, cutecom ...