2015-06-19 117 views
-1

我有3个表格 - 职位,posts_groups和组与帖子和组之间MANY_2_MANY关系。为了获得来自特定组的所有帖子,我需要加入帖子和posts_groups表。现在加入非常缓慢。我在这里描述了非常相似的情况MySQL JOIN/IN performance optimizationMysql非规范化连接表与many_2_many

我认为,为了提高性能,我需要对此结构进行非规范化。 MySQL的最佳做法是什么?我可以为帖子创建一个新的表格,我会为这些帖子参与的群组设置一些散列吗?基于这个散列,我将能够通过单选来查询来自特定组的所有帖子。如果不是的话,你能否建议最合适的方法来改善这种结构的性能?

修订

示例查询:

SELECT p.post_id, p.date_created, p.description, p.last_edited, 
     p.link, p.link_description, p.link_image_url, p.link_title, 
     p.total_comments, p.total_votes, p.type_id, p.user_id 
    FROM posts p 
    JOIN 
     (SELECT DISTINCT post_id 
      FROM posts_to_groups 
      WHERE group_id IN (1, 2, 3, 4, 5) 
    ) AS ptt USING (post_id) 
    ORDER BY p.last_edited DESC, 
       p.total_votes DESC 
    LIMIT 25 

此查询的工作快只在非并发环境 - 〜150ms的。在约50个并发用户的性能测试(JMeter)下,它显示5秒

CREATE TABLE:

CREATE TABLE `posts` (
    `post_id` int(11) NOT NULL AUTO_INCREMENT, 
    `user_id` varchar(255) NOT NULL, 
    `type_id` int(11) NOT NULL, 
    `description` text, 
    `link` varchar(1024) DEFAULT NULL, 
    `date_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `last_edited` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `total_votes` int(11) DEFAULT '0', 
    `total_comments` int(11) DEFAULT '0', 
    `link_title` varchar(1024) DEFAULT NULL, 
    `link_description` varchar(1024) DEFAULT NULL, 
    `link_image_url` varchar(1024) DEFAULT NULL, 

    PRIMARY KEY (`post_id`), 
    KEY `fk_post_type_id` (`type_id`), 
    FULLTEXT KEY `description` (`description`), 
    CONSTRAINT `fk_post_type_id` FOREIGN KEY (`type_id`) REFERENCES `post_types` (`post_type_id`) 
) 
ENGINE=InnoDB AUTO_INCREMENT=109919 DEFAULT CHARSET=utf8 

CREATE TABLE `posts_to_groups` (
    `group_id` int(11) NOT NULL, 
    `post_id` int(11) NOT NULL, 

    PRIMARY KEY (`group_id`,`post_id`), 
    KEY `post_to_groups_fk_post_id` (`post_id`), 
    CONSTRAINT `post_to_groups_fk_post_id` FOREIGN KEY (`post_id`) REFERENCES `posts` (`post_id`), 
    CONSTRAINT `post_to_groups_fk_group_id` FOREIGN KEY (`group_id`) REFERENCES `groups` (`group_id`) 
) 
ENGINE=InnoDB DEFAULT CHARSET=utf8 

CREATE TABLE `groups` (
    `group_id` int(11) NOT NULL AUTO_INCREMENT, 
    `user_id` varchar(255) NOT NULL, 
    `title` varchar(255) NOT NULL, 
    `description` text NOT NULL, 
    `date_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `total_members` int(11) NOT NULL DEFAULT '0', 
    `total_posts` int(11) NOT NULL DEFAULT '0', 

    PRIMARY KEY (`group_id`), 
    KEY `user_id_idx` (`user_id`), 
    FULLTEXT KEY `title` (`title`,`description`) 
) 
ENGINE=InnoDB AUTO_INCREMENT=1288 DEFAULT CHARSET=utf8 
+3

我们有很多的关系(Many_2_Many)和它的工作速度快,你对这些表的索引?并且您可以添加您的查询,我们会查找它 –

+0

确保您的查询正在使用**适当的索引**。很可能,您的查询可以从覆盖索引或多列索引中受益。 (“在每个单独的列上添加索引”很少是最合适的方法。) – spencer7593

+0

我更新了我的问题。另外,我有所有索引那里,细节在这里http://stackoverflow.com/questions/30872163/mysql-join-in-performance-optimization – alexanoid

回答

0

它看起来像你对我正在做一个半连接。对于通常的方法是用EXISTS表达式:

SELECT p.post_id, p.date_created, p.description, p.last_edited, 
     p.link, p.link_description, p.link_image_url, p.link_title, 
     p.total_comments, p.total_votes, p.type_id, p.user_id 
FROM posts p 
WHERE EXISTS (
     SELECT 1 
     FROM posts_to_groups 
     WHERE post_id = p.post_id 
      AND group_id IN (1, 2, 3, 4, 5) 
    ) 
ORDER BY p.last_edited DESC, 
      p.total_votes DESC 
LIMIT 25; 

或者,因为这里只有一个键字段,你可以尝试在表达:

SELECT p.post_id, p.date_created, p.description, p.last_edited, 
     p.link, p.link_description, p.link_image_url, p.link_title, 
     p.total_comments, p.total_votes, p.type_id, p.user_id 
FROM posts p 
WHERE post_id IN (
     SELECT post_id 
     FROM posts_to_groups 
     WHERE group_id IN (1, 2, 3, 4, 5) 
    ) 
ORDER BY p.last_edited DESC, 
      p.total_votes DESC 
LIMIT 25; 

中表达可能更好地工作在这取决于你数据和你正在运行的MySQL版本。旧版本在优化EXISTS时遇到问题。

在这两种情况下,我都希望在(posts.post_id)上有一个索引以及在(posts_to_groups.post_id, posts_to_groups.group_id)上的索引。


第二次尝试:

SELECT DISTINCT p.post_id, p.date_created, p.description, p.last_edited, 
     p.link, p.link_description, p.link_image_url, p.link_title, 
     p.total_comments, p.total_votes, p.type_id, p.user_id 
FROM posts p 
JOIN posts_to_groups pg 
     ON p.post_id = pg.post_od 
WHERE pg.group_id IN (1, 2, 3, 4, 5) 
ORDER BY p.last_edited DESC, 
      p.total_votes DESC 
LIMIT 25; 
+0

与大多数其他RDBMS不同,MySQL对Exists/In的性能往往低于JOIN。 –

+0

感谢您的回答,EXISTS版本〜422ms,IN - 0.170ms。现在我正在使用相同的查询(在内部SELECT中使用DISTINCT),并发环境中的结果非常慢。这就是为什么我以某种方式考虑反规范化的原因。 – alexanoid

+0

@alexanoid好的,将DISTINCT移到外部查询怎么办?它应该评估为SELECT DISTINCT,然后是ORDER BY,然后是LIMIT,因此它应该在逻辑上相同。看到我上面的更新。否则,我的猜测是你将不得不混淆临时表,这对于50个并发用户来说似乎非常严重。 –