MySQL中的Hashtag数据库设计

5
我目前正在开发一个系统,该系统将使我们的网站可以使用标签,并且在如何最好地和最有效地存储数据库中的标签方面遇到了一些问题。设计需要设置得相对简单,以便检索与搜索词匹配的帖子(类似于Twitter,当您单击标签链接时,它会显示所有带有该标签的推文)。
标签将通过从已创建帖子的内容中提取术语并插入它们来存储在数据库中(也可与Twitter相比较)。如何插入它们当然是手头的问题:
目前我在两种可能的设计之间犹豫:
1)我的第一个设计想法(可能更传统)是一个三表设计:
- 第一张表只是存储帖子内容和其他相关数据的表(我已经在使用这样的表)。 - 第二张表只是存储正在使用的新标签,基本上作为已使用的所有标签的查找表。 - 第三个表是定义标签和帖子之间关系的表。所以基本上,它将是一个简单的表,其中将具有帖子ID的一列和我们在前一个表中存储的单个标签的ID的另一列。因此,例如有3个标签的帖子将在此表中有3行,每个标签都有一个关联。
2)第二个设计是两个表的设计:
- 与上面相同的存储帖子数据的表。 - 第二个表是第一种设计中第二个和第三个表的混合体:它保存标签和帖子之间关系的数据,但是它不会将新标签存储在一个表中并为其分配ID,而是直接存储实际的标签(例如“#test”)本身以及帖子的ID。同样的概念也适用于此处,如果帖子中有3个标签,则会在表中存储3行。
我在这些想法之间犹豫,因为第一种选择似乎是更标准的方法,并且看起来更加“结构化”。然而,由于它们是标签,我认为实际上没有必要为每个标签分配唯一的ID,因为标签不像类别或流派等真正的分类。
此外,当我尝试为标签制作搜索页面时,我需要使用较少的JOIN,因为我不需要查找搜索词的ID,然后转到另一个表并查找与该ID相关联的帖子。
此外,当尝试简单列出帖子的标签时,可能会有一件有点烦人的事情,那就是标签可能会以不同于用户在其帖子中设计的样式打印出来。例如,如果用户添加了#testing,但另一个用户之前已输入了一个带有#TeStIng的帖子,则帖子的标签将打印出#TeStIng,因为它在数据库查找表中保存的方式就是这样。当然,您可以使其区分大小写,但在搜索中#testing和#TeStIng应被视为相同的标签,因此可能会变得混乱。或者我对此有误吗?有没有人对如何避免这种情况有建议?
另一方面,我对第二个表格设计的担忧是,如果表格变得非常大,那么它可能会变得效率低下,因为查找字符串比搜索整数要慢(我将使用第一个设计进行整数搜索)。然而,由于我必须在第一个设计中使用更多的JOIN,因此是否会有性能差异?仅仅为了明确,当搜索字符串本身时,我将使用=运算符而不是LIKE。
同样,我想象第一个设计在查询标签本身时更有效,例如有多少帖子正在使用某个标签等等,虽然使用第二个设计也不是很困难,但我再次关心效率。
有什么想法可能更好?最重要的是,通过标签进行搜索是有效的,因此例如我正在尝试查找与#test相关联的帖子。理想情况下,我还想能够从数据库中检索帖子的标签,因为它是由用户在帖子内容中设计的样式。在这一点上,所有其他围绕分析标签的查询和功能都是次要的。

1
采用第一种方法,因为它可以保持可扩展性。此外,对于带有原始格式的标签列表,您只需要确保显示的标签来自帖子本身,而不是列表表格(您始终可以再次从帖子中获取标签)。 - Akshat Singhal
如果我选择第一种设计,在尝试检索帖子时需要在查询中使用额外的JOIN,对于这种方法的效率有什么想法吗?即使表变得非常大,这是否可以忽略不计? - arian1123
@arian1123,要从帖子中检索原始标签,您可以使用以下方法:通过在帖子中搜索标签(将所有标签保持为小写)(strtolower)获取标签在帖子中的字符串位置,然后提取原始标签。这将使您免于使用正则表达式或通配符。 - Akshat Singhal
你真的需要使用连接吗?如果最常见的要求是获取单个标签下的所有帖子,则可以从第二个表中获取该标签的ID,并在第三个表中搜索它。这样做速度会很快。 - Akshat Singhal
@arian1123,正如Sherif所建议的那样,您可以使用Redis或类似的东西来存储标签,并快速获取ID。从ID开始,查询相应的帖子非常快,因为它将是MySQL索引的外键。即使您不想使用Redis存储,也可以直接从数据库中获取ID。我建议您进行基准测试,以评估连接是否比两个查询更好。 - Akshat Singhal
显示剩余4条评论
1个回答

5
从数据库规范化的角度来看,您的第二个设计不符合第三范式。您之所以依赖于整个主键而不仅仅是关键字,是有原因的。如果哈希表中的任何内容发生变化,对帖子表会产生直接影响,这会导致逻辑上的不一致性。例如,标签表中有两行数据:一个是标签#politics,另一个是标签#politic。假设创建第二个标签的人决定编辑他们的帖子并将标签更新为#politics(也许是因为他们打错了),那么应该更新哪一行?
关于性能,我不会担心第一种设计。你的数据库(像今天几乎所有主要的关系型DBMS一样)依赖于一种叫做二叉搜索树(或更具体地说是red-black tree)的东西,在适当索引这些值时优化数据库表格的插入/删除/搜索成本。对于某些文本搜索用例,它可以通过O(1) (哈希表查找) 进一步优化,或者您甚至可以在后面自己使用类似Memcached/Redis的键/值缓存存储进行优化。就大部分而言,索引hashtag以创建更快速的搜索使用这些hashtag的帖子是你想要采用的设计。因为最大的成本因素不在于查找单个hashtag(在这个用例中,我假设大多数搜索都只有一个hashtag),而是检索包含该hashtag的所有帖子。
关于解决您查询中不区分大小写的部分,您的数据库管理系统很可能有一些排序选项,您可以在模式中指定(例如utf8_general_ci),其中ci表示模式中不区分大小写比较。这意味着,数据将按原样存储,但在查询中与另一个值进行比较时,MySQL会以不区分大小写的方式比较字符。

非常好的回答。我只有一个关于区分大小写部分的问题:当我正在创建决定是否将新的hashtag添加到查找表中的逻辑或者它是否已经存在时,如果我使用ci,它是否会在样式不同的情况下注册一个hashtag?例如,它会同时注册"test"和"Test"。如果是这样,那么它将为本质上相同的hashtag创建2个不同的ID。然后,当我按名称搜索hashtag时,它会返回2个ID还是只有1个?如果它确实返回2个ID来匹配帖子,那么与仅搜索1个相比,它是否更低效? - arian1123
1
是的,排序规则只影响MySQL如何比较值,而不影响其如何存储值。至于您是否希望这两个哈希标签是唯一的还是不唯一的,这真的取决于您的设计。例如,可以将哈希标签以全小写形式仅存储在查找表中,而帖子本身将包含用户提交的数据。这样,哈希标签表的唯一目的就是索引。 - Sherif
我想我可能会将它们全部转换为小写字母。这似乎是一个合理的方法。挑战在于设计一种高效的方法,从帖子内容中提取标签,以便仅列出标签。在 memcache 中存储一些 ID-名称关系也是个好主意。我会接受这个答案。 - arian1123
非常有道理的@Sherif。拥有一个包含所有标签的单个表格还允许您在其旁边放置异常表格,以便将打字错误作为一个条目汇总,例如,您可能希望将Pampalona、Pamplona和Pamps的标签合并为一个标签,或者拥有一个排除表格,例如#1 #2。 - Ewen
1
在我看来,这个问题的示例是错误的。在这个示例中,我们没有更新操作,只有删除和插入操作。 - Stefan Cebulak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接