MySQL 大量行的性能问题

标签 mysql performance

我正在开发一个基于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/

相关文章:

r - 读取单列 CSV 文件的更快方法

php - 多维数组插入语句

php - 为什么 PDO::PARAM_LOB 会导致允许的内存大小耗尽错误?

mysql - 如何根据包含表名的字符串从表中选择每一行?

WCF 客户端导致服务器挂起直到连接故障

performance - 提高 jbuilder 在 rails 4.1.8 中的性能。它占用了 90% 的渲染时间

mysql - 更新mysql中的余额

mysql - 即使条目不存在也返回值

html - div容器贵吗?

java - 使用模拟器时的 keyDispatchingTimedOut