1

目前,我在 data_article_key_terms 表中有大约 900,000 个条目,用于将关键术语与其各自的文章相关联。目标是能够选择任意日期范围并根据该日期范围内的文章显示前 15 个关键术语。

我遇到的问题是我正在运行的查询需要将近 6 秒,但我需要它比这更快。我意识到这是相对于我正在运行的系统而言的,我可以使用功能更强大的机器,但在我走这条路之前,我正在尽我所能优化它。

我使用 InnoDB 作为 MySQL 存储引擎来保持数据完整性。据我了解,MyISAM 使用 count(*) 会更快,但也不能选择使用该引擎。

我还考虑过将关键术语计数存储在基于固定时间范围的表中,但这最终需要存储和跟踪大量数据。

有没有人对如何优化这种体验有好的建议?

我有以下表格:

此表存储文章信息:

CREATE TABLE `data_article` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `connection_id` int(11) NOT NULL,
  `folder_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `uid` varchar(100) NOT NULL,
  `date` date NOT NULL,
  `influencer_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `data_article_5930b15a` (`connection_id`),
  KEY `data_article_4e5f642` (`folder_id`),
  KEY `data_article_fbfc09f1` (`user_id`),
  KEY `data_article_43ae76a1` (`influencer_id`),
  KEY `data_article_date` (`date`),
  CONSTRAINT `connection_id_refs_id_b2ae9152` FOREIGN KEY (`connection_id`) REFERENCES `account_connection` (`id`),
  CONSTRAINT `folder_id_refs_id_e343586a` FOREIGN KEY (`folder_id`) REFERENCES `account_folder` (`id`),
  CONSTRAINT `influencer_id_refs_id_45cd3615` FOREIGN KEY (`influencer_id`) REFERENCES `data_influencer` (`id`),
  CONSTRAINT `user_id_refs_id_aca13cc9` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)
)

此表存储关键术语:

CREATE TABLE `data_keyterm` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `term` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `data_keyterm_term` (`term`)
)

此表存储文章和关键术语之间的关系:

CREATE TABLE `data_article_key_terms` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `article_id` int(11) NOT NULL,
  `keyterm_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `article_id` (`article_id`,`keyterm_id`),
  KEY `data_article_key_terms_30525a19` (`article_id`),
  KEY `data_article_key_terms_1d848ca4` (`keyterm_id`),
  CONSTRAINT `article_id_refs_id_d87be8f5` FOREIGN KEY (`article_id`) REFERENCES `data_article` (`id`),
  CONSTRAINT `keyterm_id_refs_id_50d233f8` FOREIGN KEY (`keyterm_id`) REFERENCES `data_keyterm` (`id`)
)

此表存储与文章关联的影响者:

CREATE TABLE `data_influencer` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `title` varchar(100) NOT NULL,
  `email` varchar(100) NOT NULL,
  `active` tinyint(1) NOT NULL,
  `user_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `data_influencer_fbfc09f1` (`user_id`),
  KEY `data_influencer_name` (`name`),
  CONSTRAINT `user_id_refs_id_b1bb5d4f` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)
)

这是我用来根据时间范围提取关键字、对它们进行分组并按频率排序的 SQL 语句:

SELECT dk.id, dk.term as term, COUNT(dk.id) as count
FROM data_keyterm dk
INNER JOIN data_article_key_terms dakt ON dakt.keyterm_id = dk.id
INNER JOIN data_article da ON da.id = dakt.article_id
INNER JOIN data_influencer di ON di.id = da.influencer_id
WHERE da.user_id = 1
AND da.date between '2010-08-07' AND '2012-08-07'
AND di.active = True
GROUP BY dk.id
ORDER BY count DESC
LIMIT 15;
4

2 回答 2

0

使用具有 900,000 条记录和 3 个内部联接的表运行内部联接将需要一些时间来执行。我认为您应该尝试一些外部搜索引擎,例如太阳能,以快速获得结果

于 2012-08-07T14:58:38.303 回答
0

我想知道在这种情况下,索引是否对您没有帮助。查询的选择性是什么?也就是说,使用了多少条文章/组合键?

为了优化性能,我认为查询计划应该是按用户 ID 和日期选择文章,然后进行连接。然后将这个简化的子集用于其他连接。相反,我怀疑它自始至终都在使用索引。

我的第一个建议是用单个索引替换文章表上用户 ID/日期的两个索引。WHERE 子句可以使用这个单一的索引来满足条件。这可以简化和改进查询计划。

Another thing to test is denormalizing the article/key table a bit. Assuming that the keys and article are created at the same time, add the user id and date to this table. Then, simply rephrase your query as restrictions on this table. You can then have a composite index on user id and date. However, I don't suggest having separate indexes on these fields.

于 2012-08-07T16:06:12.593 回答