6

使用数据库模式从这个问题的已接受答案中进行标记是否可以使用 group_concat 进行查询以处理大量数据?我需要为所有带有标签 x 的项目获取带有标签的项目。使用具有约 50 万个标签的 group_concat 查询在 > 15 秒时非常慢。如果没有 group_concat (没有标签的项目),它是 ~ 0.05 秒。

作为一个附带问题,SO如何解决这个问题?

4

4 回答 4

5

这可能是索引策略不佳的情况。调整您链接到的问题的已接受答案中显示的架构:

CREATE Table Items (
  Item_ID    SERIAL,
  Item_Title VARCHAR(255),
  Content    TEXT
) ENGINE=InnoDB;

CREATE TABLE Tags (
  Tag_ID     SERIAL,
  Tag_Title  VARCHAR(255)
) ENGINE=InnoDB;

CREATE TABLE Items_Tags (
  Item_ID    BIGINT UNSIGNED REFERENCES Items (Item_ID),
  Tag_ID     BIGINT UNSIGNED REFERENCES Tags  ( Tag_ID),
  PRIMARY KEY (Item_ID, Tag_ID)
) ENGINE=InnoDB;

注意:

  • MySQL 的SERIAL数据类型是一个别名BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,因此是索引的;

  • 定义外键约束Items_Tags在外键列上创建索引。

于 2013-05-20T09:18:25.390 回答
3

我建议在规范化数据和非规范化数据之间进行混合。
因此,使用eggyal提供的规范化结构,我将执行以下非规范化结构:

CREATE TABLE Items_Tags_Denormalized (
  Item_ID    BIGINT UNSIGNED REFERENCES Items (Item_ID),
  Tags     BLOB,
  PRIMARY KEY (Item_ID)
) ENGINE=InnoDB;

在列Tags中,您将拥有对应的所有标签 ( Tag_Title) Item_ID
现在您有 2 种方法可以实现这一目标:

  • 创建一个定期运行的 cron,它将Items_Tags_Denormalized使用GROUP_CONCAT或任何适合您的方式构建此表(优点:在表中插入或删除时不会增加额外的负载Items_Tags;缺点:非规范化表并不总是最新的(取决于多久你运行cron))

  • 在插入和删除时为表创建触发器Items_Tags以使表保持最新Items_Tags_Denormalized(优点:非规范化表将始终是最新的;缺点:在Items_Tags表中插入或删除时会增加负载)

考虑到优缺点,选择最适合您需求的解决方案。

所以最后你会得到Items_Tags_Denormalized一张表,你只会从中读取而不做额外的操作

于 2013-05-20T11:31:33.147 回答
1

你为什么要使用 group_concat 呢?对于给定的标签 x,您说选择项目列表很快。对于给定的项目列表,获取所有标签也应该很快。并且通常没有某种限制,我的意思是普通网站不会在一页上显示 100000 个条目。

我会建议:

drop temporary table if exists lookup_item;

create temporary table lookup_item (item_id serial, primary key(item_id));

insert into lookup_item select i.id as item_id 
from items i 
where exists (select * from items_tags where item_id = i.id and tag_id = <tag_id>)
and <other conditions or limits>;

select * from lookup_item
inner join items_tags it on it.item_id = i.id
inner join tags t on t.id = it.tag_id
order by i.<priority>, t.<priority>

优先级可以是项目的最后修改,标签的某种重要性。

然后你会得到每件带有标签的物品。代码中唯一的工作是查看结果行何时具有下一项。

于 2013-05-25T12:29:42.033 回答
1

如果我理解正确,GROUP_CONCAT那么您要删除的不仅仅是使查询在没有标签的情况下更快的事情。在GROUP_CONCAT您选择Tags.Tag_Title并强制访问标签表的内部。

你可以试着GROUP_CONCAT用跑步Items_Tags.Tag_ID来测试我的理论。

于 2013-05-27T02:57:32.840 回答