
标签云系统必须用三张表,不能只靠 articles 表加 tags 字段
硬编码标签到 articles 表的 tags 字段(比如存成逗号分隔字符串),看似简单,实际会让查询、统计、去重全部失效。你没法高效查“同时打上「MySQL」和「性能优化」的文章”,也没法统计“哪个标签被用了最多次”。
必须拆成三张表:articles、tags、article_tags(中间表)。中间表里只有两个字段:article_id 和 tag_id,联合主键或加唯一索引防重复。
查标签云数据时,别直接 GROUP BY tag_name,先 JOIN 再 COUNT
如果 tags 表里存的是 name,而中间表只存 ID,那统计每个标签的使用次数就不能绕过 JOIN。常见错误是先查出所有 tags,再循环查每条的计数——这会触发 N+1 查询,几百个标签就卡死。
正确写法是单条 SQL:
SELECT t.name, COUNT(at.article_id) AS cnt<br>FROM tags t<br>LEFT JOIN article_tags at ON t.id = at.tag_id<br>GROUP BY t.id, t.name<br>ORDER BY cnt DESC<br>LIMIT 50;注意:用
LEFT JOIN 是为了保留零使用次数的标签(如果业务需要);GROUP BY 必须包含 t.id,否则 MySQL 8.0+ 严格模式会报错。
中间表没加索引?查得慢、删标签还锁表
article_tags 表看着简单,但没索引就是性能黑洞。默认只靠主键(比如 (article_id, tag_id))并不能加速按标签查文章,或者按文章删所有标签。
至少要补两个索引:
-
INDEX idx_tag_id (tag_id):支撑“查某标签下所有文章”或“统计标签热度” -
INDEX idx_article_id (article_id):支撑“删除某文章所有标签”或“查某文章打了哪些标签”
DELETE FROM article_tags WHERE tag_id = ?,没索引就会全表扫描,大表下可能锁住整个中间表几秒。
标签名重复怎么办?用唯一约束 + INSERT IGNORE 而不是 SELECT 判断
用户输入“mysql”“MySQL”“MYSQL”,本质是同一个标签,但数据库里会存三条。解决方法不是靠应用层反复查重,而是让数据库兜底。
在 tags 表上建:
UNIQUE INDEX uk_name_lower (LOWER(name))然后插入新标签时用:
INSERT IGNORE INTO tags (name) VALUES ('MySQL');这样大小写变体自动去重。注意:LOWER(name) 索引在 MySQL 5.7+ 支持函数索引,8.0+ 更稳;5.6 及更早版本得加一个计算列或靠应用层统一转小写后再插。
中间表的生命周期管理最容易被忽略——没人清理孤立标签(即 tags 表里有记录,但 article_tags 里没关联任何文章)。这类脏数据积累多了,标签云里就会出现“无人问津”的幽灵标签。要不要定时清理,得看业务是否允许“预置标签”;如果允许,就得额外加标记字段,不能光靠存在性判断。


















