0

我正在开发一个基于 XenForo 引擎的站点,并且在获取所有线程并连接帖子表和论坛表以获取该线程所属的第一个帖子和论坛的一些信息的查询中遇到问题。查询如下所示:

SELECT thread . *
FROM xf_thread AS thread
INNER JOIN xf_node AS node ON (node.node_id = thread.node_id)
INNER JOIN xf_post AS post ON (post.post_id = thread.first_post_id)
WHERE thread.node_id IN ('295', '296', '297', '298', '299', '300', '301', '302', '256', '2575', '258', '259', '260', '253', '254', '255', '127', '163', '159', '144', '145', '146', '147', '148', '164', '165', '166', '167', '168', '169', '170', '162', '171', '173', '172', '128', '129', '130', '131', '132', '133', '134', '135', '136', '137', '138', '139', '140', '141', '142', '143', '151', '152', '153', '154', '155', '157', '156', '158', '161', '160', '149', '227', '232', '237', '233', '236', '234', '235', '238', '248', '240', '241', '242', '239', '246', '247', '243', '244', '245', '228', '229', '230', '231', '249', '250', '251', '174', '190', '195', '199', '193', '191', '197', '198', '192', '200', '204', '207', '205', '203', '206', '202', '208', '201', '187', '176', '177', '178', '189', '188', '180', '186', '184', '185', '182', '183', '181', '179', '209', '211', '217', '218', '219', '210', '212', '213', '214', '215', '216', '220', '222', '223', '224', '221', '225', '261', '291', '276', '272', '270', '265', '277', '267', '286', '292', '289', '274', '264', '287', '278', '282', '279', '281', '280', '283', '284', '285', '290', '275', '268', '263', '266', '294', '262', '293', '269', '273', '288', '271')
ORDER BY thread.last_post_date DESC
LIMIT 10

查询的解释结果是:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE 节点索引 PRIMARY PRIMARY 4 NULL 199 使用 where;使用索引;使用临时的;使用文件排序
1 简单线程参考 node_id_last_post_date,node_id_sticky_last_post_date node_id_last_post_date 4 node.node_id 221  
1 SIMPLE post eq_ref PRIMARY PRIMARY 4 thread.first_post_id 1 使用索引

查询需要 9+ 秒才能执行。

删除 xf_node 表的连接,在 0.01 秒内运行查询。解释看起来像

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE 线程索引 node_id_last_post_date,node_id_sticky_last_post_da... last_post_date 4 NULL 69970 使用 where
1 SIMPLE post eq_ref PRIMARY PRIMARY 4 thread.first_post_id 1 使用索引

删除 xf_post 表的连接在 0.01 秒内运行查询,解释看起来像

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE 线程索引 node_id_last_post_date,node_id_sticky_last_post_da... last_post_date 4 NULL 70840 使用 where
1 SIMPLE 节点 eq_ref PRIMARY PRIMARY 4 thread.node_id 1 使用索引

因此,仅当两个表都连接时才存在问题,但连接本身似乎完全正确并且可以完美地分开工作。

表中的行数 - xf_thread: 71,855, xf_node: 178, xf_post: 2,977,326

我的假设是,当两个表都连接时,MySQL 开始使用不正确的索引,也许强制索引会解决问题?

非常感谢您为找到解决此问题的方法提供的帮助和建议。

编辑:这里是所有涉及的表的创建表语句

xf_node

CREATE TABLE `xf_node` (  
  `node_id` int(10) unsigned NOT NULL auto_increment,  
  `title` varchar(50) NOT NULL,  
  `description` text NOT NULL,  
  `node_name` varchar(50) default NULL COMMENT 'Unique column used as string ID by some node types',  
  `node_type_id` varbinary(25) NOT NULL,  
  `parent_node_id` int(10) unsigned NOT NULL default '0',  
  `display_order` int(10) unsigned NOT NULL default '1',  
  `display_in_list` tinyint(3) unsigned NOT NULL default '1' COMMENT 'If 0, hidden from node list. Still counts for lft/rgt.',  
  `lft` int(10) unsigned NOT NULL default '0' COMMENT 'Nested set info ''left'' value',  
  `rgt` int(10) unsigned NOT NULL default '0' COMMENT 'Nested set info ''right'' value',  
  `depth` int(10) unsigned NOT NULL default '0' COMMENT 'Depth = 0: no parent',  
  `style_id` int(10) unsigned NOT NULL default '0' COMMENT 'Style override for specific node',  
  `effective_style_id` int(10) unsigned NOT NULL default '0' COMMENT 'Style override; pushed down tree',  
  PRIMARY KEY  (`node_id`),  
  UNIQUE KEY `node_name_unique` (`node_name`,`node_type_id`),  
  KEY `parent_node_id` (`parent_node_id`),  
  KEY `display_order` (`display_order`),  
  KEY `display_in_list` (`display_in_list`,`lft`),  
  KEY `lft` (`lft`)  
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=304 ;  

xf_post

CREATE TABLE `xf_post` (  
  `post_id` int(10) unsigned NOT NULL auto_increment,  
  `thread_id` int(10) unsigned NOT NULL,  
  `user_id` int(10) unsigned NOT NULL,  
  `username` varchar(50) NOT NULL,  
  `post_date` int(10) unsigned NOT NULL,  
  `message` mediumtext NOT NULL,  
  `ip_id` int(10) unsigned NOT NULL default '0',  
  `message_state` enum('visible','moderated','deleted') NOT NULL default 'visible',  
  `attach_count` smallint(5) unsigned NOT NULL default '0',  
  `position` int(10) unsigned NOT NULL,  
  `likes` int(10) unsigned NOT NULL default '0',  
  `like_users` blob NOT NULL,  
  `warning_id` int(10) unsigned NOT NULL default '0',  
  `warning_message` varchar(255) NOT NULL default '',  
  PRIMARY KEY  (`post_id`),  
  KEY `thread_id_post_date` (`thread_id`,`post_date`),  
  KEY `thread_id_position` (`thread_id`,`position`),  
  KEY `user_id` (`user_id`)  
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3123657 ;  

xf_thread

CREATE TABLE `xf_thread` (  
  `thread_id` int(10) unsigned NOT NULL auto_increment,  
  `node_id` int(10) unsigned NOT NULL,  
  `title` varchar(150) NOT NULL,  
  `reply_count` int(10) unsigned NOT NULL default '0',  
  `view_count` int(10) unsigned NOT NULL default '0',  
  `user_id` int(10) unsigned NOT NULL,  
  `username` varchar(50) NOT NULL,  
  `post_date` int(10) unsigned NOT NULL,  
  `sticky` tinyint(3) unsigned NOT NULL default '0',  
  `discussion_state` enum('visible','moderated','deleted') NOT NULL default 'visible',  
  `discussion_open` tinyint(3) unsigned NOT NULL default '1',  
  `discussion_type` varchar(25) NOT NULL default '',  
  `first_post_id` int(10) unsigned NOT NULL,  
  `first_post_likes` int(10) unsigned NOT NULL default '0',  
  `last_post_date` int(10) unsigned NOT NULL,  
  `last_post_id` int(10) unsigned NOT NULL,  
  `last_post_user_id` int(10) unsigned NOT NULL,  
  `last_post_username` varchar(50) NOT NULL,  
  `prefix_id` int(10) unsigned NOT NULL default '0',  
  PRIMARY KEY  (`thread_id`),  
  KEY `node_id_last_post_date` (`node_id`,`last_post_date`),  
  KEY `node_id_sticky_last_post_date` (`node_id`,`sticky`,`last_post_date`),  
  KEY `last_post_date` (`last_post_date`)  
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=76301 ;  

谢谢你。

4

4 回答 4

0

有时 mysql 中的 order by 子句会使用临时表对结果进行排序。对于大数据,可能需要很长时间。避免使用“order by desc”并在 mysql 之外对数据进行排序。

于 2013-05-19T07:54:08.477 回答
0

您发布的查询不使用 post 表中的任何列,也未在过滤器中使用。我会说你可以放弃它。

对于 DESC 排序(这很耗时):如果您创建一个额外的列并填充它,则可以将其调整为 ASC

MAX_INT - unix_timestamp(thread.last_post_date)

这将为您以后的线程提供更小的数字,因此 ASC 订单可以让您真正按时获得 DESC。

在这种情况下,还必须创建新索引。

(示例是伪语言,请检查 mysql 的语法)

于 2013-05-19T08:25:29.010 回答
0

Maby 尝试类似的方法并在评论中显示您的结果:

SELECT thread . *, post.*
FROM xf_thread AS thread
INNER JOIN xf_node AS node ON (node.node_id = thread.node_id)    
JOIN (
    SELECT * 
    FROM xf_post AS p
    WHERE p.post_id IN (
        SELECT t.first_post_id FROM thread t WHERE t.node_id IN ('295', ...)
    )
) AS post ON thread.first_post_id = post.post_id
WHERE thread.node_id IN ('295', ...)
ORDER BY thread.last_post_date DESC
LIMIT 10
于 2013-05-19T13:03:29.120 回答
0

如果两thread列 (node_idfirst_post_id) 不可为空,并且您有FOREIGN KEY可以信任的约束,从thread(node_id)朝向xf_node (node_id)和从thread(first_post_id)朝向xf_post(post_id),您可以删除与xf_nodeand的连接xf_post

SELECT thread . *
FROM xf_thread AS thread
WHERE thread.node_id IN (295, 296, ..., 271)
ORDER BY thread.last_post_date DESC
LIMIT 10 ;

上的索引(last_post_date, node_id)也可能有助于提高效率:

ALTER TABLE xf_thread 
  ADD INDEX last_post_date_node_id 
    (last_post_date, node_id) ;
于 2013-05-19T08:42:59.133 回答