MySQL查询所需的时间比首次运行时要长(但仅在选择某个字节阈值时)

时间:2012-08-15 21:51:08

标签: php mysql performance

我第一次在页面加载时运行此查询,它总是需要大约100毫秒,即使它应该需要大约1.我通过My​​SQL控制台和PhpMyAdmin运行完全相同的查询,它总是很快。

我已经在PHP中定时了:

$t = microtime(true);
mysql_query($sql, $this->id);
die((microtime(true)-$t)*1000);

如果我第二次运行完全相同的查询(例如通过复制中间行),第二次几乎立即运行。

查询如下所示:

SELECT `user_id`, `login`, `first_name`, `last_name`, `name`, `email`, ... FROM `users` WHERE `user_id`='1000' LIMIT 1

通过实验,我发现如果减少我选择的字段数量,它会突然快速运行。当我添加第26列时,它从1ms跳到100ms。第26列似乎并不重要,即使它是一个像“1”这样的常数,它也会突然变慢。

可能导致这种情况的原因,我该如何解决?


架构:

CREATE TABLE `user` (
    `f1` int(11) unsigned NOT NULL DEFAULT '0',
    `f2` varchar(50) NOT NULL DEFAULT '',
    `f3` varchar(32) NOT NULL DEFAULT '',
    `f4` varchar(50) DEFAULT NULL,
    `f5` varchar(50) DEFAULT NULL,
    `f6` varchar(100) NOT NULL DEFAULT 'Anonymous',
    `f7` varchar(100) DEFAULT NULL,
    `f8` varchar(100) NOT NULL DEFAULT 'Anonymous',
    `f9` tinyint(1) NOT NULL DEFAULT '0',
    `f10` tinyint(1) unsigned NOT NULL DEFAULT '0',
    `f11` varchar(250) DEFAULT NULL,
    `f12` int(10) unsigned NOT NULL DEFAULT '0',
    `f13` varchar(250) NOT NULL DEFAULT '',
    `f14` int(10) unsigned NOT NULL DEFAULT '0',
    `f15` varchar(100) DEFAULT NULL,
    `f16` varchar(25) DEFAULT NULL,
    `f17` varchar(25) DEFAULT NULL,
    `f18` varchar(25) DEFAULT NULL,
    `f19` varchar(200) DEFAULT NULL,
    `f20` varchar(50) DEFAULT NULL,
    `f21` varchar(50) DEFAULT 'BC',
    `f22` varchar(50) DEFAULT 'Canada',
    `f23` varchar(20) DEFAULT NULL,
    `f24` bigint(20) unsigned NOT NULL DEFAULT '0',
    `f25` bigint(20) unsigned NOT NULL DEFAULT '0',
    `f26` int(5) unsigned zerofill NOT NULL DEFAULT '00000',
    `f27` tinyint(1) NOT NULL DEFAULT '0',
    `f28` tinyint(1) unsigned NOT NULL DEFAULT '0',
    `f29` tinyint(1) NOT NULL DEFAULT '0',
    `f30` tinyint(1) unsigned NOT NULL DEFAULT '0',
    `f31` varchar(50) DEFAULT NULL,
    `f32` varchar(100) NOT NULL DEFAULT '',
    `f33` bigint(20) unsigned NOT NULL DEFAULT '0',
    `f34` bigint(20) unsigned NOT NULL DEFAULT '0',
    `f35` varchar(250) DEFAULT NULL,
    `f36` text NOT NULL,
    `f37` tinyint(1) unsigned NOT NULL DEFAULT '0',
    `f38` bigint(20) NOT NULL DEFAULT '0',
    `f39` bigint(20) NOT NULL DEFAULT '0',
    `f40` tinyint(1) unsigned NOT NULL DEFAULT '0',
    `f41` tinyint(1) unsigned NOT NULL DEFAULT '0',
    PRIMARY KEY (`f1`),
    UNIQUE KEY `f2` (`f2`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

我改变了字段名称以保护无辜者。


更新:我可能误解了魔力限制。我认为这毕竟是一个字节限制...我尝试了所有数字,除了我实际需要的少数几个字段:

SELECT `f1`, `f2`, `f3`, `f4`, `f5`, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55 FROM `wx_user` WHERE `user_id`='1000' LIMIT 1

运行时间为1毫秒。如果我在末尾添加“56”,则需要约100毫秒。


我做了更多测试:

mysql_query("SELECT 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' FROM `user` WHERE `f1`='1000' LIMIT 1");

介于1200到1300之间时,它会从1毫秒跳到100毫秒。

我可以使用更多列重复相同的事情,但是更小的数据(整数)。

这对我来说有两件事:

  1. 它与发送的字节数有关
  2. 添加额外列的开销非常大,因为选择大约50个整数与选择单个1300字段具有相同的效果。
  3. 现在我考虑一下,这个数字与John建议的数字非常接近。 1300个字符+开销可能等于John提到的1500 MTU限制。

    这是MySQL设置还是操作系统设置?我可以试验一下吗?

3 个答案:

答案 0 :(得分:2)

MySQl缓存您的查询,因此第二次执行它时,它会从缓存加载并且速度会快得多。

还要确保在您的桌子上有正确的索引,因为这可以加快很多事情。

答案 1 :(得分:2)

正如约翰所述,同一查询的第二次运行从缓存中检索其结果,因此它几乎是即时的。

据说,在某些情况下,查询可能“运行”的时间比添加第X列时的时间长。几种可能性:

  • 第X列非常大(如CHAR(65000)),传输它需要时间,
  • 所有(X-1)列都是已存在于MySQL内存中的索引的一部分,但是对第X列的引用迫使MySQL从驱动器读取行,
  • 同时查询锁定引用表或导致CPU / hdd / memory上的负载,
  • 第X列是TEXT / BLOB,必须进行额外的磁盘搜索以从外部文件中检索列值(BLOB和TEXT值存储在表数据文件之外),
  • 第X列为行数据添加了足够的字节,必须从MySQL服务器发送到应用程序,必须使用两个或多个网络数据包来传输整行。如果你真的,真的,我的意思是**真的**必须从查询中挤出每毫秒确保查询结果适合一个MTU帧(通常是1500字节) - 使用嗅探器来确认它,
  • 其他情况水平相当低,正常人在现实世界中看不到它们。

您还可以使用内置的MySQL查询分析来找出哪个查询执行阶段花费了这么多时间。运行:

SET profiling = 1;
[..run Your queries, each one is numbered..]
SHOW PROFILE FOR QUERY n; -- SHOW PROFILE FOR QUERY 1;
SET profiling = 0;

如果花费额外的时间,你应该有很好的洞察力。在此处阅读有关查询概要分析的更多信息:MySQL Query Profiling

答案 2 :(得分:2)

嗯,MySQL有一个缓存并且速度很快......

但不仅如此。问题不在您的查询中。我遇到了同样的事情,我无法调试它的原因正是缓存:我不能再次产生同样的问题了。它不仅仅是MySQL,它也是缓存事物的文件系统,所以它完全不可能重现最初的缓慢 - 而且我不确定它是否会发生第二次,也许背后的原因导致它不再发生。试图重新启动MySQL,尝试刷新缓存,但没有任何东西可以重现第一次运行。

还有什么更神秘的!有一个没用的解决办法:如果你在查询之前做一个CHECK TABLE(并确保它只是'第一次!),查询本身将以正常速度运行。不,与缓存时的速度不一样;这是一个正常但仍然是第一时间的速度。如你所愿,立即再次调用它会产生结果。

现在每个人都点头并说"当然,文件系统在执行CHECK时会缓存表文件,以便下次运行得更快"。 但事实并非如此。如果我尝试使用任何其他程序将MYD文件读入内存(我做了一个简单的"复制"),它就没有&#39 ; t解决问题,即使我也复制了索引。但是,检查似乎确实有所帮助。

结论: - 这不是一个简单的慢查询"问题,这是黑暗的巫术。显然,这种解决方法不适用于生产环境。我只是提到它,因为它显示了两件重要的事情:

  1. 区别不仅仅是缓存。还有更多。
  2. 在开发时间内,您可以对问题进行简单的修复。
  3. 希望这可以帮助一些人 - 带我好几天:)