加个order by使得查询效率提高500倍

很简单的三个表:

p248_user记录用户信息

CREATE TABLE `p248_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`list_ids` varchar(4000) NOT NULL DEFAULT ‘‘,
`email` varchar(255) NOT NULL,
`mobile` varchar(20) NOT NULL,
`_created` datetime NOT NULL,
`_updated` datetime NOT NULL,
`hb_status` tinyint(4) DEFAULT ‘0‘,
`sb_status` tinyint(4) DEFAULT ‘0‘,
`unsubscribe_email_status` tinyint(4) DEFAULT ‘0‘,
`unsubscribe_sms_status` tinyint(4) DEFAULT ‘0‘,
`hb_time` datetime DEFAULT NULL,
`unsubscribe_email_time` datetime DEFAULT NULL,
`unsubscribe_sms_time` datetime DEFAULT NULL,
`_create_operator_name` varchar(100) DEFAULT NULL,
`_update_operator_name` varchar(100) DEFAULT NULL,
`_create_operator_email` varchar(100) DEFAULT NULL,
`_update_operator_email` varchar(100) DEFAULT NULL,
`name` varchar(255) NOT NULL DEFAULT ‘‘,
`time` varchar(255) NOT NULL DEFAULT ‘‘,
`year` int(11) NOT NULL DEFAULT ‘0‘,
PRIMARY KEY (`id`),
UNIQUE KEY `u1` (`email`,`mobile`) USING BTREE,
KEY `_updated` (`_updated`),
KEY `mobile` (`mobile`)
) ENGINE=InnoDB AUTO_INCREMENT=5596286 DEFAULT CHARSET=utf8

p248_list记录组信息

CREATE TABLE `p248_list` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`status` enum(‘active‘,‘delete‘) DEFAULT ‘active‘,
`_created` datetime NOT NULL,
`_updated` datetime NOT NULL,
`user_count` int(11) DEFAULT ‘0‘,
`lock_status` int(11) NOT NULL DEFAULT ‘0‘,
`lock_reason` varchar(100) DEFAULT NULL,
`lock_time` datetime DEFAULT NULL,
`import_percent` int(11) DEFAULT NULL,
`hb_count` int(11) DEFAULT ‘0‘,
`sb_count` int(11) DEFAULT ‘0‘,
`unsubscribe_email_count` int(11) DEFAULT ‘0‘,
`unsubscribe_sms_count` int(11) DEFAULT ‘0‘,
`_create_operator_name` varchar(100) DEFAULT NULL,
`_update_operator_name` varchar(100) DEFAULT NULL,
`_create_operator_email` varchar(100) DEFAULT NULL,
`_update_operator_email` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `_updated` (`_updated`)
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8

p248_user_list是个多对多的表,记录用户属于哪些组

CREATE TABLE `p248_user_list` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`list_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_list_id` (`user_id`,`list_id`),
KEY `list_id` (`list_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5646298 DEFAULT CHARSET=utf8

p248_user有200万条记录, p248_user_list有1000万条记录。

现在要找出属于29分组,并且手机号码不为空,并且没有退订的用户。这样的用户大约有100万个。现在要把这些用户按照4000个一批放到一群临时的记录集里。

这个要用到分页了,一开始的想法:

第一页:

SELECT `id`, `email`, `mobile`, `_created`, `_updated`, `_create_operator_name`, `_update_operator_name`, `name`, `time`, `year` FROM `p248_user` WHERE 1 = 1 AND id IN (SELECT DISTINCT user_id FROM `p248_user_list` WHERE list_id IN (29)) AND unsubscribe_sms_status = 0 AND mobile <> ‘‘ LIMIT 0, 4000;

第二页就LIMIT 4000, 4000。第三页就LIMIT 8000, 4000。依次类推。

结果这个SQL查询耗时用了整整5秒。

分析一下这个查询:

mysql> explain SELECT `id`, `email`, `mobile`, `_created`, `_updated`, `_create_operator_name`, `_update_operator_name`, `name`, `time`, `year` FROM `p248_user` WHERE 1 = 1 AND id IN (SELECT DISTINCT user_id FROM `p248_user_list` WHERE list_id IN (29)) AND unsubscribe_sms_status = 0 AND mobile <> ‘‘ LIMIT 0, 4000;
+----+-------------+----------------+--------+----------------------+--------------+---------+-----------------------------+--------+------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------+--------+----------------------+--------------+---------+-----------------------------+--------+------------------------------------+
| 1 | SIMPLE | p248_user | range | PRIMARY,mobile | mobile | 62 | NULL | 934446 | Using index condition; Using where |
| 1 | SIMPLE | p248_user_list | eq_ref | user_list_id,list_id | user_list_id | 8 | contacts.p248_user.id,const | 1 | Using index |
+----+-------------+----------------+--------+----------------------+--------------+---------+-----------------------------+--------+------------------------------------+
2 rows in set (0.00 sec)

可以看到用户表扫描了93万行,几乎是全表扫描了。也就是把所有符合条件的结果都取了出来然后再取前4000条。

把上面的查询加上了ORDER BY `id`,结果查询耗时仅0.01秒,查询速度足足提高了500倍。

为什么会这样呢?

分析一下新的查询:

mysql> explain SELECT `id`, `email`, `mobile`, `_created`, `_updated`, `_create_operator_name`, `_update_operator_name`, `name`, `time`, `year` FROM `p248_user` WHERE 1 = 1 AND id IN (SELECT DISTINCT user_id FROM `p248_user_list` WHERE list_id IN (29)) AND unsubscribe_sms_status = 0 AND mobile <> ‘‘ ORDER BY `id` LIMIT 0, 4000;
+----+-------------+----------------+--------+----------------------+--------------+---------+-----------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------+--------+----------------------+--------------+---------+-----------------------------+------+-------------+
| 1 | SIMPLE | p248_user | index | PRIMARY,mobile | PRIMARY | 4 | NULL | 7999 | Using where |
| 1 | SIMPLE | p248_user_list | eq_ref | user_list_id,list_id | user_list_id | 8 | contacts.p248_user.id,const | 1 | Using index |
+----+-------------+----------------+--------+----------------------+--------------+---------+-----------------------------+------+-------------+
2 rows in set (0.00 sec)

这次用户表仅扫描了8000行。也就是查询先使用了主键索引,扫描完前4000条符合条件的记录就直接结束了。

那取第二页呢:

mysql> explain SELECT `id`, `email`, `mobile`, `_created`, `_updated`, `_create_operator_name`, `_update_operator_name`, `name`, `time`, `year` FROM `p248_user` WHERE 1 = 1 AND id IN (SELECT DISTINCT user_id FROM `p248_user_list` WHERE list_id IN (29)) AND unsubscribe_sms_status = 0 AND mobile <> ‘‘ ORDER BY `id` LIMIT 4000, 4000;
+----+-------------+----------------+--------+----------------------+--------------+---------+-----------------------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------+--------+----------------------+--------------+---------+-----------------------------+-------+-------------+
| 1 | SIMPLE | p248_user | index | PRIMARY,mobile | PRIMARY | 4 | NULL | 15999 | Using where |
| 1 | SIMPLE | p248_user_list | eq_ref | user_list_id,list_id | user_list_id | 8 | contacts.p248_user.id,const | 1 | Using index |
+----+-------------+----------------+--------+----------------------+--------------+---------+-----------------------------+-------+-------------+
2 rows in set (0.00 sec)

这次就要扫描16000行了,因为前4000条是第一页的没用扔掉了。

这样的话页数越大查询就会越耗时。

但实际上可以换个方法:

第一次查询结束时,得到最后一条记录的user id, 比如是6500。

第二次查询的时候用这个user_id作为条件去匹配

SELECT `id`, `email`, `mobile`, `_created`, `_updated`, `_create_operator_name`, `_update_operator_name`, `name`, `time`, `year` FROM `p248_user` WHERE id > 6500 AND id IN (SELECT DISTINCT user_id FROM `p248_user_list` WHERE list_id IN (29)) AND unsubscribe_sms_status = 0 AND mobile <> ‘‘ ORDER BY `id` LIMIT 0, 4000;

这样扫描的行数和第一页依然是一样的。

直到最后一页也是如此,耗时不会有任何明显的下降。

时间: 2024-10-22 05:35:01

加个order by使得查询效率提高500倍的相关文章

提高Order by语句查询效率的两个思路

提高Order by语句查询效率的两个思路 2011-03-01 13:07 水太深 ITPUB 字号:T | T 在MySQL数据库中,Order by语句的使用频率是比较高的.但是众所周知,在使用这个语句时,往往会降低数据查询的性能.因为可能需要对数据库的记录进行重新排序.在这篇文章中,笔者就谈谈提高Order By语句查询效率的两个思路,以供大家参考. AD: 在MySQL数据库中,Order by语句的使用频率是比较高的.但是众所周知,在使用这个语句时,往往会降低数据查询的性能.因为可能

HDOJ 题目3966 Aragorn&#39;s Story(Link Cut Tree成段加减点权,查询点权)

Aragorn's Story Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5505    Accepted Submission(s): 1441 Problem Description Our protagonist is the handsome human prince Aragorn comes from The Lor

一个让业务开发效率提高10倍的golang库

一个让业务开发效率提高10倍的golang库 此文除了是标题党,没有什么其他问题. 这篇文章推荐一个库,https://github.com/jianfengye/collection. 这个库是我在开发业务过程中 Slice 的频繁导致业务开发效率低,就产生了要做一个 Collection 包的想法.本文说说我开发这个库的前因后果 Golang 适不适合写业务? 最近一个逻辑非常复杂的业务,我用 Golang 来开发.开发过程不断在问一个问题,Golang 适不适合写业务? 业务说到底,是一大

股价飙升500倍,市值超过4700亿美元,从网络书店起家的亚马逊凭什么一飞冲天?

砺石导语:亚马逊股价上周创出999.00美元的历史最高价,距1000美元的关口仅有一步之遥.从一家建在自家车库里的网络图书公司,到被媒体称为"吞噬世界的怪物",亚马逊崛起的背后有着怎样的征途? 文|文媛媛 如果你在亚马逊IPO的时候投资1万美元,那么现在你会得到500万美元. 作为美国最大的电子商务公司,亚马逊股价上周创出999.00美元的历史最高价,距1000美元的关口仅有一步之遥.上周五,亚马逊股价报收于995.78美元,市值达到4730亿美元. 亚马逊公司1995年成立,是世界上

mysql查询性能问题,加了order by速度慢了

关于order by的查询优化可以看一下: MySQL ORDER BY/LIMIT performance: late row lookups 主要介绍了两个方法: 第一个是FORCE INDEX (PRIMARY):这个理解很直白就是强行加索引 第二个是late row lookups,也就是文章的重点,其实就是先构造一个只有id的子查询,然后再join一起.这样极大的提高效率.如下示例代码,o是通过你的表和只有id查询出来的临时字表,l是要join一起包含所有字段的表. explain S

百万数据查询效率提高方法(转)

原文地址:https://www.cnblogs.com/eer123/p/9875844.html 处理百万级以上的数据提高查询速度的方法: 1.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描. 2.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 3.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:     select id fr

PHP,Mysql-根据一个给定经纬度的点,进行附近地点查询–合理利用算法,效率提高2125倍

目前的工作是需要对用户的一些数据进行分析,每个用户都有若干条记录,每条记录中有用户的一个位置,是用经度和纬度表示的.还有一个给定的数据库,存储的是一些已知地点以及他们的经纬度,内有43W多条的数据.现在需要拿用户的经纬度和已知地点进行距离匹配,如果它们之间的距离小于一定的数据,比如说500米,就认为用户是在这个地点.MYSQL本身是支持空间索引的,但是在5.x的版本中,取消了对Distance()和Related()的支持,参考这里:MySQL 5.1参考手册 :: 19. 中的空间扩展 19.

ASP.NET MVC搭建项目后台UI框架—11、自动加载下拉框查询

ASP.NET MVC搭建项目后台UI框架—1.后台主框架 需求:在查询记录的时候,输入第一个字,就自动把以这个字开头的相关记录查找出来,输入2个字就过滤以这两个子开头的记录,依次类推. 突然要用到这个功能了,印象中曾经写过这个功能的文章,一下子找不到了,只好重新贴出来备忘.最近博客快2个月没更新了,因为这两个月一直在闭门写书. 引入js和css下载地址:http://download.csdn.net/detail/zouyujie1127/9550279 <link href="~/l

mysql有意思的order by 子查询

在对应的mysql技术小伙伴交流群里发现一个这样的order by 需求:根据学生的平均成绩排序 按我以往的经验,我肯定 select t1.* from student t1 join (select sno,avg(score) avg_score from student group by t1.sno) t2 on t1.sno=t2.sno order by t2.avg_score 然后发现群里还可以这样,也算是涨见识了吧,没想到order by里也可以这样用,以前都是用在 wher