2

我第一次在页面加载时运行这个查询,它总是需要大约 100 毫秒,即使它应该需要大约 1 毫秒。我已经通过 MySQL 控制台和 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 列时,它从 1 毫秒跳到 100 毫秒。第 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 a 之间的某个地方,它从 1 毫秒跳到 100 毫秒。

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

这向我暗示了两件事:

  1. 它与发送的字节数有关
  2. 添加额外列的开销非常大,因为选择大约 50 个整数与选择单个 1300 个字符字段的效果相同。

现在我想起来,这个数字非常接近约翰建议的数字。1300 个字符 + 开销可能等于 John 提到的 1500 MTU 限制。

这是 MySQL 设置还是操作系统设置?我可以尝试什么?

4

3 回答 3

2

正如约翰所说,第二次运行相同的查询从缓存中检索结果,所以它几乎是即时的。

话虽如此,在某些情况下,当添加第 X 列时,查询可能“运行”的时间比它应该运行的时间长。几种可能:

  • 第 X 列非常大(如 CHAR(65000)),传输它需要时间,
  • 所有(X-1)列都是已经在 MySQL 内存中的索引的一部分,但是对 Xth 列的引用会强制 MySQL 从驱动器中读取行,
  • 同时查询锁定引用的表或导致 CPU/hdd/内存负载,
  • 第 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 查询分析

于 2012-08-15T22:37:54.233 回答
2

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

还要确保在您的表上有正确的索引,因为这可以大大加快速度。

于 2012-08-15T22:00:02.517 回答
2

嗯,MySQL 有一个缓存,它可以加快速度,这一切都是真的……

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

还有更神秘的!有一个没有意义的解决方法:如果您在查询之前执行 CHECK TABLE(并确保它只是第一次!),查询本身将以正常速度运行。不,与已经缓存时的速度不同;这是一个正常但仍然是第一次的速度。如您所料,再次调用它会立即产生结果。

现在每个人都点头说“当然,文件系统在执行 CHECK 时会缓存表文件,因此下次运行速度会更快”。 但事实并非如此。 如果我尝试使用任何其他程序将 MYD 文件读入内存(我做了一个简单的“复制”),它并不能解决问题,即使我也复制了索引。然而,CHECK 似乎做了一些真正有帮助的事情。

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

  1. 不同之处不仅在于缓存。还有更多。
  2. 在开发期间,您可以针对该问题应用简单的修复程序。

希望这对那里的一些人有所帮助-花了我几天时间:)

于 2014-04-10T14:33:58.677 回答