mysql - 简单查询需要15-30秒

标签 mysql performance limit

下面的查询非常简单。它从消息表中选择最后 20 条记录用于分页场景。第一次运行此查询需要 15 到 30 秒。随后的运行不到一秒钟(我预计会涉及一些缓存)。我正在尝试确定为什么第一次需要这么长时间。

这是查询:

SELECT DISTINCT ID,List,`From`,Subject, UNIX_TIMESTAMP(MsgDate) AS FmtDate
FROM messages
WHERE List='general'
ORDER BY MsgDate
LIMIT 17290,20;

MySQL版本:4.0.26-log

这是表格:

messages  CREATE TABLE `messages` (
  `ID` int(10) unsigned NOT NULL auto_increment,
  `List` varchar(10) NOT NULL default '',
  `MessageId` varchar(128) NOT NULL default '',
  `From` varchar(128) NOT NULL default '',
  `Subject` varchar(128) NOT NULL default '',
  `MsgDate` datetime NOT NULL default '0000-00-00 00:00:00',
  `TextBody` longtext NOT NULL,
  `HtmlBody` longtext NOT NULL,
  `Headers` text NOT NULL,
  `UserID` int(10) unsigned default NULL,
  PRIMARY KEY  (`ID`),
  UNIQUE KEY `List` (`List`,`MsgDate`,`MessageId`),
  KEY `From` (`From`),
  KEY `UserID` (`UserID`,`List`,`MsgDate`),
  KEY `MsgDate` (`MsgDate`),
  KEY `ListOnly` (`List`)
) TYPE=MyISAM ROW_FORMAT=DYNAMIC

这里是解释:

table   type    possible_keys  key       key_len  ref       rows  Extra
------  ------  -------------  --------  -------  ------  ------  --------------------------------------------
m       ref     List,ListOnly  ListOnly  10       const    18002  Using where; Using temporary; Using filesort

当我在所有相关列上都有索引时,为什么要使用文件排序?我添加了 ListOnly 索引只是为了看看它是否有帮助。我本来以为List索引可以同时处理MsgDate上的列表选择和排序,但是并没有。现在我添加了 ListOnly 索引,这是它使用的索引,但它仍然对 MsgDate 进行文件排序,我怀疑这花费了这么长时间。

我尝试使用 FORCE INDEX 如下:

SELECT DISTINCT ID,List,`From`,Subject, UNIX_TIMESTAMP(MsgDate) AS FmtDate
FROM messages
FORCE INDEX (List)
WHERE List='general'
ORDER BY MsgDate
LIMIT 17290,20;

这似乎确实强制 MySQL 使用索引,但它根本不会加快查询速度。

下面是这个查询的解释:

table   type    possible_keys  key     key_len  ref       rows  Extra                       
------  ------  -------------  ------  -------  ------  ------  ----------------------------
m       ref     List           List    10       const    18002  Using where; Using temporary

更新:

我从查询中删除了 DISTINCT。它对性能没有任何帮助。

我删除了 UNIX_TIMESTAMP 调用。它也不会影响性能。

我在我的 PHP 代码中做了一个特例,这样如果我检测到用户正在查看结果的最后一页,我会添加一个仅返回最近 7 天结果的 WHERE 子句:

SELECT m.ID,List,From,Subject,MsgDate
FROM messages
WHERE MsgDate>='2009-11-15'
ORDER BY MsgDate DESC
LIMIT 20

这要快得多。但是,一旦我导航到另一页结果,它必须使用旧的 SQL 并且需要很长时间才能执行。我想不出一种实用、现实的方法来对所有页面执行此操作。此外,执行这种特殊情况会使我的 PHP 代码更加复杂。

奇怪的是,只有第一次运行原始查询需要很长时间。随后运行相同的查询或显示不同结果页的查询(即,只有 LIMIT 子句发生变化)都非常快。如果大约 5 分钟未运行,查询将再次变慢。

解决方案:

我想出的最佳解决方案是基于 Jason Orendorff 和 Juliet 的想法。

首先,我确定当前页面是否更接近总页数的开头或结尾。如果接近末尾,我使用 ORDER BY MsgDate DESC,应用适当的限制,然后反转返回记录的顺序。

这使得检索接近结果集开头或结尾的页面更快(现在第一次需要 4-5 秒,而不是 15-30 秒)。如果用户想要导航到靠近中间的页面(目前大约是第 430 页),那么速度可能会回落。但这种情况很少见。

因此,虽然似乎没有完美的解决方案,但在大多数情况下这比以前要好得多。

谢谢杰森和朱丽叶。

最佳答案

代替ORDER BY MsgDate LIMIT 17290,20,试试ORDER BY MsgDate DESC LIMIT 20

当然结果会以相反的顺序出来,不过那应该很容易处理。

编辑:您的MessageId 值是否总是随时间增加?它们是独一无二的吗?

如果是这样,我会做一个索引:

UNIQUE KEY `ListMsgId` ( `List`, `MessageId` )

并尽可能根据消息 ID 而不是日期进行查询。

-- Most recent messages (in reverse order)
SELECT * FROM messages
WHERE List = 'general'
ORDER BY MessageId DESC
LIMIT 20

-- Previous page (in reverse order)
SELECT * FROM messages
WHERE List = 'general' AND MessageId < '15885830'
ORDER BY MessageId DESC
LIMIT 20

-- Next page
SELECT * FROM messages
WHERE List = 'general' AND MessageId > '15885829'
ORDER BY MessageId
LIMIT 20

我认为您还需要为 varchar 列付费,其中 int 类型会快得多。例如,List 可以改为指向单独表中条目的 ListId。您可能想在测试数据库中尝试一下,看看是否真的如此;我不是 MySQL 专家。

关于mysql - 简单查询需要15-30秒,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1778865/

相关文章:

javascript - 如何使用php和mysql过滤highcharts中的日期

ruby-on-rails - Rails 3 缓存 - 是否可以根据特定模型更改使页面/操作缓存过期?

ubuntu - Apache2限制下载速度

我的代码中的 MySQL 语法错误,我无法手动找到

mysql - SQL转置和groupby

mysql - 你会如何设计一个表来记录用户在mysql中的好恶?

c# - 托管在 Windows 服务中的 WCF 服务运行速度比控制台应用程序慢 10 倍

Python:在大 dbf (xbase) 文件中快速查询

c# - 32 位 .NET 最大字节数组大小是否小于 2GB?

scala - Spark Scala 2.10 元组限制