使用数据库模式从这个问题的已接受答案中进行标记是否可以使用 group_concat 进行查询以处理大量数据?我需要为所有带有标签 x 的项目获取带有标签的项目。使用具有约 50 万个标签的 group_concat 查询在 > 15 秒时非常慢。如果没有 group_concat (没有标签的项目),它是 ~ 0.05 秒。
作为一个附带问题,SO如何解决这个问题?
使用数据库模式从这个问题的已接受答案中进行标记是否可以使用 group_concat 进行查询以处理大量数据?我需要为所有带有标签 x 的项目获取带有标签的项目。使用具有约 50 万个标签的 group_concat 查询在 > 15 秒时非常慢。如果没有 group_concat (没有标签的项目),它是 ~ 0.05 秒。
作为一个附带问题,SO如何解决这个问题?
这可能是索引策略不佳的情况。调整您链接到的问题的已接受答案中显示的架构:
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
在外键列上创建索引。
我建议在规范化数据和非规范化数据之间进行混合。
因此,使用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
一张表,你只会从中读取而不做额外的操作。
你为什么要使用 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>
优先级可以是项目的最后修改,标签的某种重要性。
然后你会得到每件带有标签的物品。代码中唯一的工作是查看结果行何时具有下一项。
如果我理解正确,GROUP_CONCAT
那么您要删除的不仅仅是使查询在没有标签的情况下更快的事情。在GROUP_CONCAT
您选择Tags.Tag_Title
并强制访问标签表的内部。
你可以试着GROUP_CONCAT
用跑步Items_Tags.Tag_ID
来测试我的理论。