我想知道实现标签系统的最佳方法是什么,就像在 SO 上使用的那样。我正在考虑这一点,但我想不出一个好的可扩展解决方案。
我正在考虑有一个基本的 3 表解决方案:有一张tags
桌子,一张articles
桌子和一张tag_to_articles
桌子。
这是解决这个问题的最佳方法,还是有替代方案?使用这种方法,表格会及时变得非常大,我认为搜索效率不是很高。另一方面,快速执行查询并不那么重要。
您的三表解决方案没有任何问题。
另一种选择是限制可应用于一篇文章的标签数量(如 SO 中的 5 个)并将它们直接添加到您的文章表中。
规范化数据库有其优点和缺点,就像将事物硬连接到一张表中一样有优点和缺点。
没有什么说你不能两者兼得。重复信息与关系数据库范式背道而驰,但如果目标是性能,您可能必须打破这些范式。
您提出的三表实现将适用于标记。
然而,堆栈溢出使用不同的实现。他们以纯文本形式将标签存储到帖子表中的 varchar 列,并使用全文索引来获取与标签匹配的帖子。例如posts.tags = "algorithm system tagging best-practices"
. 我确信杰夫在某处提到过这一点,但我忘记了在哪里。
提议的解决方案是我能想到的解决标签和文章之间多对多关系的最佳方法——如果不是唯一可行的话。所以我的投票是“是的,它仍然是最好的”。不过,我会对任何替代品感兴趣。
如果您的数据库支持可索引数组(例如 PostgreSQL),我会推荐一个完全非规范化的解决方案 - 将标签作为字符串数组存储在同一个表上。如果没有,将对象映射到标签的辅助表是最佳解决方案。如果您需要针对标签存储额外信息,您可以使用单独的标签表,但是为每个标签查找引入第二个连接是没有意义的。
我想建议优化 MySQLicious 以获得更好的性能。在此之前 Toxi (3 table) 解决方案的缺点是
如果您有数百万个问题,并且每个问题有 5 个标签,那么 tagmap 表中将有 500 万个条目。因此,首先我们必须根据标签搜索过滤掉 10000 个 tagmap 条目,然后再次过滤掉这 10000 个匹配的问题。因此,虽然过滤掉艺术 id 是否是简单的数字,那么它是可以的,但如果它是一种 UUID(32 varchar),那么过滤掉需要更大的比较,尽管它是索引的。
我的解决方案:
每当创建新标签时,使用 counter++(以 10 为底),并将该计数器转换为 base64。现在每个标签名称都将具有 base64 id。并将此 ID 与名称一起传递给 UI。这样,您将拥有最多两个字符 ID,直到我们在系统中创建 4095 个标签。现在将这些多个标签连接到每个问题表标签列中。也添加分隔符并使其排序。
所以表看起来像这样
查询时,查询 id 而不是真实的标签名称。由于它是SORTED,and
标签上的条件将更有效(LIKE '%|a|%|c|%|f|%
)。
请注意,单个空格分隔符是不够的,我们需要双分隔符来区分标签,例如sql
andmysql
因为LIKE "%sql%"
也会返回mysql
结果。应该LIKE "%|sql|%"
我知道搜索是非索引的,但您仍然可能已在与文章相关的其他列(如作者/日期时间)上建立索引,否则将导致全表扫描。
最后,使用此解决方案,不需要内部连接,在连接条件下必须将数百万条记录与 500 万条记录进行比较。
CREATE TABLE Tags (
tag VARHAR(...) NOT NULL,
bid INT ... NOT NULL,
PRIMARY KEY(tag, bid),
INDEX(bid, tag)
)
笔记:
AUTO_INCREMENT
PK。因此,它比 Scuttle 更好。LIKE
带有前导通配符;子字符串的错误命中)相关讨论(针对 MySQL):
many:many 映射表优化
有序列表