【问题标题】:Scalable Database Tagging Schema可扩展的数据库标记模式
【发布时间】:2010-10-14 10:30:51
【问题描述】:

编辑:致构建标签系统的人。不要读这个。这不是你要找的。当我不知道RDBMS都有自己的优化方法时,我问了这个问题,只需使用简单的多对多方案即可。

我有一个拥有数百万个帖子的发帖系统。每个帖子可以有无数个与之关联的标签。

用户可以创建包含注释、创建日期、所有者等的标签。标签几乎就像帖子本身,因为人们可以发布关于标签的注释。

每个标签关联都有一个所有者和日期,因此我们可以看到谁添加了标签以及何时添加。

我的问题是如何实现这一点?它必须通过标签或标签快速搜索帖子。此外,用户可以通过在字段中输入名称来为帖子添加标签,有点像谷歌搜索栏,它必须为您填写标签名称的其余部分。

目前我有 3 个解决方案,但不确定哪个是最好的,或者是否有更好的方法。

请注意,我没有显示笔记的布局,因为一旦我获得了适当的标签解决方案,这将是微不足道的。

方法一、链表

post中的tagId指向tag_assoc中的一个链表,应用程序必须遍历链表直到flink=0

post:           id, content, ownerId, date, tagId, notesId
tag_assoc:      id, tagId, ownerId, flink
tag:            id, name, notesId

方法2.反规范化

tags 只是一个 VARCHAR 或 TEXT 字段,其中包含一个制表符分隔的 tagId:ownerId 数组。它不能是固定大小。

post:           id, content, ownerId, date, tags, notesId
tag:            id, name, notesId

方法3.毒

(来自:http://www.pui.ch/phred/archives/2005/04/tags-database-schemas.html, 这里也一样:Recommended SQL database design for tags or tagging)

post:          id, content, ownerId, date, notesId
tag_assoc:     ownerId, tagId, postId
tag:           id, name, notesId

方法 3 提出了一个问题,遍历 tag_assoc 中的每一行的速度有多快?

方法1和2对于post返回标签应该很快,但是对于标签post,必须另外做一个查找表。

我最不需要担心的是按名称优化搜索标签,我还没有解决这个问题。

我在这里做了一个ASCII图:http://pastebin.com/f1c4e0e53

【问题讨论】:

    标签: sql database performance tags scalability


    【解决方案1】:

    我会这样做:

    posts:          [postId], content, ownerId, date, noteId, noteType='post'
    tag_assoc:      [postId, tagName], ownerId, date, noteId, noteType='tagAssoc'
    tags:           [tagName], ownerId, date, noteId, noteType='tag'
    notes:          [noteId, noteType], ownerId, date, content
    

    方括号中的字段是各个表的主键。

    在每个表中定义noteType 的约束:poststag_assoctags。例如,这可以防止给定的注释同时应用于posttag

    将标签名称存储为短字符串,而不是整数 id。这样您就可以在 tag_assoc 表中使用覆盖索引 [postId, tagName]。

    标记完成是通过 AJAX 调用完成的。如果用户为标签键入“datab”,您的网页会进行 AJAX 调用,并且在服务器端,应用会查询:SELECT tagName FROM tags WHERE tagName LIKE ?||'%'

    【讨论】:

      【解决方案2】:

      “标签几乎就像帖子本身,因为人们可以发布关于标签的注释。” - 这句话让我觉得你真的只想要一个用于 POST 的表,带有一个主键和一个引用 POST 表的外键。现在,您可以在磁盘空间允许的范围内为每个帖子添加尽可能多的标签。

      我假设 POST 和标签之间不需要多对多,因为标签不会在帖子之间共享,基于此:

      “用户可以创建带有注释、创建日期、所有者等的标签。”

      如果创建日期和所有者是共享的,那将是两个额外的外键关系,IMO。

      【讨论】:

      • 标签在帖子之间共享。我现在几乎决定了方法3。每个可以有标签的表都会有另一个名为 _tags 的表。例如:news_tags。这个方法我还是有点粗略,但似乎每个人都推荐它,所以我假设 MySQL 会优化它。
      • "假设" - 坏主意。知道更好。
      【解决方案3】:

      链表几乎肯定是错误的方法。这当然意味着您的查询将是复杂的或次优的 - 这是具有讽刺意味的,因为使用链表的最可能原因是将数据保持在正确的排序顺序中。但是,我没有看到一种简单的方法来避免迭代地获取一行,然后使用检索到的 flink 值来调整下一行的选择操作。

      因此,请使用基于表的方法,将普通外键用于主键引用。 Bill Karwin 概述的那个看起来与我概述的相似。

      【讨论】:

        【解决方案4】:

        Bill 我想我有点把你甩了,笔记就在另一个桌子上,还有一个单独的桌子,上面有不同人发布的笔记。帖子有注释和标签,但标签也有注释,这就是标签是唯一的原因。

        Jonathan 对链表的看法是正确的,我根本不会使用它们。我决定以最简单的规范化方式实现标签,以满足我的需求:

        DROP TABLE IF EXISTS `tags`;
        CREATE TABLE IF NOT EXISTS `tags` (
          `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
          `owner` int(10) unsigned NOT NULL,
          `date` int(10) unsigned NOT NULL,
          `name` varchar(255) NOT NULL,
          PRIMARY KEY (`id`),
          UNIQUE KEY `name` (`name`)
        ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
        
        DROP TABLE IF EXISTS `posts`;
        CREATE TABLE IF NOT EXISTS `posts` (
          `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
          `owner` int(10) unsigned NOT NULL,
          `date` int(10) unsigned NOT NULL,
          `name` varchar(255) NOT NULL,
          `content` TEXT NOT NULL,
          PRIMARY KEY (`id`)
        ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
        
        DROP TABLE IF EXISTS `posts_notes`;
        CREATE TABLE IF NOT EXISTS `posts_notes` (
          `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
          `owner` int(10) unsigned NOT NULL,
          `date` int(10) unsigned NOT NULL,
          `postId` int(10) unsigned NOT NULL,
          `note` TEXT NOT NULL,
          PRIMARY KEY (`id`),
          FOREIGN KEY (`postId`) REFERENCES posts(`id`) ON DELETE CASCADE
        ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
        
        DROP TABLE IF EXISTS `posts_tags`;
        CREATE TABLE IF NOT EXISTS `posts_tags` (
          `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
          `owner` int(10) unsigned NOT NULL,
          `tagId` int(10) unsigned NOT NULL,
          `postId` int(10) unsigned NOT NULL,
          PRIMARY KEY (`id`),
          FOREIGN KEY (`postId`) REFERENCES posts(`id`) ON DELETE CASCADE,
          FOREIGN KEY (`tagId`) REFERENCES tags(`id`) ON DELETE CASCADE
        ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
        

        我不确定这在未来会有多快,但在一段时间内应该没问题,因为只有几个人使用数据库。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-10-13
          • 2011-02-14
          • 1970-01-01
          • 2011-02-04
          • 2012-12-05
          • 1970-01-01
          • 2010-12-07
          相关资源
          最近更新 更多