我正在开发一个基于XenForo引擎的网站,并且在获取所有线程并连接帖子表和论坛表以获取该线程所属的第一个帖子和论坛的一些信息的查询中遇到问题。查询如下:
SELECT thread . *<br/>
FROM xf_thread AS thread<br/>
INNER JOIN xf_node AS node ON (node.node_id = thread.node_id)<br/>
INNER JOIN xf_post AS post ON (post.post_id = thread.first_post_id)<br/>
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')<br/>
ORDER BY thread.last_post_date DESC<br/>
LIMIT 10
解释查询结果为:
id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE node index PRIMARY PRIMARY 4 NULL 199 Using where; Using index; Using temporary; Using filesort 1 SIMPLE thread ref 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 Using index
Query takes 9+ seconds to execute.
Removing joining of xf_node table, runs the query in 0.01 seconds. Explain looks like
id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE thread index node_id_last_post_date,node_id_sticky_last_post_da... last_post_date 4 NULL 69970 Using where 1 SIMPLE post eq_ref PRIMARY PRIMARY 4 thread.first_post_id 1 Using index
Removing joining of xf_post table runs the query in 0.01 seconds, explain looks like
id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE thread index node_id_last_post_date,node_id_sticky_last_post_da... last_post_date 4 NULL 70840 Using where 1 SIMPLE node eq_ref PRIMARY PRIMARY 4 thread.node_id 1 Using index
So, the problem exists only when both tables are joined, but joins themselves seem to be completely correct and work perfectly separately.
Number of rows in the tables - xf_thread: 71,855, xf_node: 178, xf_post: 2,977,326
My assumption is that when both tables are joined MySQL starts to use incorrect indexes, and maybe forcing an index will solve the problem?
Your help and suggestions to find a way to resolve this problem are highly appreciated.
EDIT: Here are create table statements for all tables involved
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 ;
谢谢。
最佳答案
有时mysql中的order by子句会使用临时表对结果进行排序。对于大数据,可能需要花费大量时间。避免使用“order by desc”并在 mysql 之外对数据进行排序。
关于MySQL 大量行的性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16632379/