Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 简单查询需要15-30秒_Mysql_Performance_Limit - Fatal编程技术网

Mysql 简单查询需要15-30秒

Mysql 简单查询需要15-30秒,mysql,performance,limit,Mysql,Performance,Limit,下面的查询非常简单。它从messages表中选择最后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;

下面的查询非常简单。它从messages表中选择最后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索引只是为了看看它是否有用。我原本以为列表索引可以处理MsgDate上的列表选择和排序,但事实并非如此。现在我添加了ListOnly索引,这就是它使用的索引,但它仍然在MsgDate上进行文件排序,我怀疑这需要很长时间

我尝试使用力索引,如下所示:

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\u时间戳调用。这也不会影响性能


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

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页),则速度可能会下降。但这将是一个罕见的案例

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


谢谢你们,杰森和朱丽叶。

你们使用的是什么版本的SQL?一些旧版本使用LIMIT子句作为后期处理过滤器(这意味着从服务器获取所有请求的记录,但只显示您请求返回的20条记录)


您可以从您的解释中看到,18002行正在返回,尽管您只显示了其中的20行。有没有办法调整您的选择标准以确定要返回的20行,而不是返回18000行并只显示其中的20行?

请尝试
按MsgDate限制17290,20排序
,而不是
按MsgDate描述限制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
——最近的消息(按相反顺序)
从消息中选择*
其中List='general'
按消息ID描述订购
限制20
--上一页(按相反顺序)
从消息中选择*
其中列表='general'和MessageId<'15885830'
按消息ID描述订购
限制20
--下一页
从消息中选择*
其中列表='general'和MessageId>'15885829'
按消息ID订购
限制20

我想你也为拥有
varchar
列付出了代价,而
int
类型会快得多。例如,
List
可以改为一个
ListId
,它指向单独表中的一个条目。您可能想在测试数据库中尝试一下,看看这是否真的是真的;我不是MySQL专家。

您可以删除
ListOnly
键。复合索引
列表
已包含其中的所有信息


您对
列表
索引查询的解释看起来好多了,因为缺少文件排序。您可以按照Jason的建议交换顺序,并可能丢失UNIX_时间戳调用(您可以在应用程序层这样做,或者只使用存储在模式中的整数形式的UNIX时间戳),从而获得更好的实际性能。

4.0.26:肯定是比我现在希望使用的MySQL更旧的版本。这很有意义,第一个查询返回18002行,尽管它只显示其中的20行。(你的解释说明了这一点)。看起来其他人也提供了同样的解决方案,尝试通过WHERE子句返回几行,而不是依赖LIMIT选项。如果您正在寻找最新的记录,您可能希望获取max msgdate,然后在where子句中使用它,可能还有之前的日期。尽管这是两个查询,一个用于获取最大日期,另一个用于数据,但它应该运行得更快。Good luckI在我的PHP代码中做了一个特例,因此如果我检测到用户正在查看最后一页的结果,我会添加一个WHERE子句,该子句只返回最后7天的结果:SELECT m.ID,List,
From
,Subject,来自消息m的MsgDate,其中MsgDate>='2009-11-15'命令由MsgDate描述限制20这要快得多。然而,一旦我浏览到r的另一页