2013-01-03 166 views
2

我有一个项目,管理员需要创建多个通讯,其中包含一些来自网络的已抓取帖子。如何在大型表上优化这个mysql连接?

我在抓取完成后在posts表格中插入帖子,并为它们分配feed_id以标识源码。这是posts表的结构(部分):

CREATE TABLE `posts` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `feed_id` int(11) NOT NULL, 
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `updated_at` timestamp NULL DEFAULT NULL, 
    `identifier` varchar(255) DEFAULT NULL, 
    `published` timestamp NULL DEFAULT NULL, 
    `content` longtext, 
    ... 
    ... 
    `is_unread` int(1) NOT NULL DEFAULT '1', 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

每个管理员(用户)可以访问一个或多个“饲料”。因此,在新闻快讯创建页面中,我想向他们展示他们可以看到的新闻帖子列表,并且还显示了一个按钮,可以将该帖子置于该新闻快讯的特定类别中,如果用户以前选择该帖子,我应该告诉他,让他从类别中删除它。所以我也有其他一些表格:newsletters,categories,newsletter_post,category_post。这里是它们的结构:

newsletters

CREATE TABLE `newsletters` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `updated_at` timestamp NULL DEFAULT NULL, 
    `sent_at` timestamp NULL DEFAULT NULL, 
    `title` varchar(255) DEFAULT NULL, 
    `date` date DEFAULT NULL, 
    `topic_id` int(11) NOT NULL, 
    `user_id` int(11) NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

categories

CREATE TABLE `categories` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `topic_id` int(11) NOT NULL, 
    `title` varchar(255) DEFAULT NULL, 
    `slug` varchar(255) DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

newsletter_post

CREATE TABLE `newsletter_post` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `updated_at` timestamp NULL DEFAULT NULL, 
    `newsletter_id` int(11) NOT NULL, 
    `post_id` int(11) NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

category_post

CREATE TABLE `category_post` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `updated_at` timestamp NULL DEFAULT NULL, 
    `category_id` int(11) NOT NULL, 
    `post_id` int(11) NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

所以我用这个查询找到了允许的Feed职位,检查状态,如果帖子是在这个特定的通讯特定类别:

SELECT DISTINCT `posts`.`id`, `published`, `posts`.`title`, `posts`.`content`, `source_name`, `category_id`, `newsletter_id`, `link_href`, categories.title as category_title 
FROM `posts` 
LEFT JOIN `category_post` ON `posts`.`id` = `category_post`.`post_id` 
LEFT JOIN `categories` ON `categories`.`id` = `category_post`.`category_id` 
LEFT JOIN `newsletter_post` ON `posts`.`id` = `newsletter_post`.`post_id` 
LEFT JOIN `newsletters` ON `newsletters`.`id` = `newsletter_post`.`newsletter_id` 
WHERE `feed_id` IN (6, 7) ORDER BY `posts`.`published` DESC LIMIT 40 OFFSET 0 

但问题是,这是可怕的并没有优化。我的posts表每月最多包含50,000行,每行平均有3〜10kbs的数据,所以有时当我尝试运行查询时(管理员经常运行这些查询以创建新闻简报,分页等)mysql显示这个错误:太多的行加入等等,大部分时间它真的很慢。

和我在一个查询中完成所有操作的原因是因为我希望结果在一个json响应中,因此我可以快速向用户显示它们而不会执行其他请求。

我想知道是否有更好的方法来做这个查询或使用索引或其他东西。 非常感谢您的帮助。

+1

请粘贴说明计划 –

+0

@SashiKant这是解释计划:http://d.pr/i/UKgh(帖子表包含〜2500行现在) – Sallar

回答

2

索引你的职位表中的以下指标指标上

(FEED_ID,出版)

所以数据的WHERE子句已经优化,预排序以帮助您的ORDER BY。

+0

只需要像普通索引Sashi上面提到的其他桌子? – Sallar

+2

是的,你的表中的索引也应该基于你期望在正常基础上得到结果的标准/顺序......除了需要连接到表的pk/fk的那些标准/顺序。但是即使对其他表的索引也可以是多列索引,如果你加入了前表:TableA.SomeKey = TableB.ForeignSomeKey AND TableB.OtherField ='SomeStatus'...你的TableB将受益于(ForeignSomeKey,OtherField)的索引for加入。 – DRapp

+0

谢谢,还有一个问题,如果我需要基于“已发布”以外的内容进行排序?像source_name?因为管理员有选择排序类型 – Sallar

1

请创建::

1) `post_id` in `category_post` 
2) `post_id` in `newsletter_post` 
+0

我添加了索引,并且查询得到了比之前:0.1 vs 0.9s。我是否需要将索引添加到'feed'中的'feed_id'和'published',因为我在WHERE和ORDER BY中使用了这些列? – Sallar

+1

@SallarKaboli:看看这些列让数据库引擎扫描所有行,它们的索引是非常重要的。关于您提到的列的索引,您可以粘贴更新后的解释计划,以便我可以检查是否可以再次优化 –

+0

这里是更新说明:http://d.pr/i/udt4 – Sallar

2

对于有很多需求的查询查询,InnoDB效率很低。我建议你使用NoSQL数据库,但是如果你不想要或改变的代价太大......你可以试试这个:

1)LIKE Sallar Kaboli告诉你,你必须在JOIN查询中使用的列索引你的表。例如:

 CREATE INDEX index1 ON newsletter_post (post_id); 

2)仅使用JOINS的重要列。

我的意思是,你必须只使用查询的SELECT部分​​中使用的列。

我希望这会有所帮助。

+0

MongoDB会是一个不错的选择吗? – Sallar

+1

是的,这对您的情况会非常有帮助。首先,你必须分析变化和学习的成本。很多人认为MongoDB是所有问题的解决方案,但不是。但我认为可以对您的特定情况有所帮助。 – Cesar

1

完成其他的答案,我建议改变posts表此类型:

1)改变feed_idint(4)。真的,你有超过int(4)提要?
2)将is_unread更改为bit而不是int(1)。我应该说,这可能不会改善您提出的问题查询,但根据字段名称,正确的类型是bit

对此答案的另一个改进是,从不使用数字或ID字段的默认int(11),指定更具体的类型。使用较小尺寸的类型也会改善您的索引。我不认为你需要超过int(4)字段id。

例如索引和查询int(3)列比int(11)更快。

+0

谢谢Afshin,我从来没有用过'bit',所以我不知道它存在:) – Sallar

+0

@SallarKaboli欢迎Sallar Jan :)希望这会有所帮助。 –

+0

此答案包含有关整数数据类型的一些错误信息。括号内的数字不影响用于存储它的字节数。您应该使用适当的整数类型,例如INT','SMALLINT','TINYINT'等,如果你想减少用于存储该值的字节数。请参阅https://stackoverflow.com/a/4055587/1220706 – antonagestam