【问题标题】:An efficient database design for a simple forum using php and mysql使用 php 和 mysql 的简单论坛的高效数据库设计
【发布时间】:2013-11-29 19:01:24
【问题描述】:

我正在为我的网站设计一个用于论坛功能的数据库。在 SO 和 google 上进行了一些搜索之后,我想出了以下设计: 用户表

Username : varchar(256)
Password : varchar(256)

线程表

ThreadId  :  int
UserId    :  int, related to Users table
Title     :  varchar(255)
Date      :  timestamp, when a thread was created

帖子表

PostId   :   int
ThreadId :   int, related to Threads table
UserId   :   int, related to Users table
Date     :   timestamp, when post was made
Title    :   varchar(255) - post title (optional)
Body     :   text - the actual body of a post

尽管这符合我的目的,但我不禁认为这不是很有效,特别是对于需要遍历整个表的特定线程选择所有帖子时。

从我的脑海中,我可以想到一个设计,其中 Users 表Threads 表 保持原样,而不是只有一个表Posts 表,我为每个与用户同名的用户创建一个 Posts 表。这样检索属于一个线程的所有帖子效率更高,因为我只需要启动线程的人的用户 ID。使用此信息,我搜索具有相同名称的表以检索特定线程的所有帖子。但是直接创建表的数量是否是个好主意取决于注册用户的数量?我还想知道的是,这些设计中的哪一种会更好地扩展,更容易管理?有更好的数据库设计来满足我的要求吗?

【问题讨论】:

标签: php mysql database optimization


【解决方案1】:

你的设计看起来基本正确。

这是一个经典的“规范化” 数据结构 - 正是关系数据库所构建的那种形状。如果您不了解范式,但想出了这种结构,那么您显然对关系数据库的工作原理有一个自然的了解。

http://en.wikipedia.org/wiki/Database_normalization#Normal_forms

为了让 PHP 避免遍历整个表,您应该确保您发出一个只选择您正在寻找的记录的 SQL 语句。例如

SELECT * FROM posts WHERE ThreadId = ? ORDER BY Date

您担心数据库必须遍历整个表是公平合理的,尽管您可以避免这种情况 - 这是一个经典的关系数据库问题,当它们在 30 多年前首次作为商业产品出现时就已解决。

您可以在帖子上创建一个索引,以支持您正在运行的 SQL。在这种情况下,大致如下:

CREATE INDEX postThreadsIndex ON posts ( ThreadId, Date )

此索引允许您的数据库引擎快速找到您选择的记录,而无需读取整个表格。如果你想知道怎么做,请阅读 b-tree 索引。

http://en.wikipedia.org/wiki/B-tree

正如我之前在回答中所说,这正是关系数据库的构建目标,您的设计是可靠且适当的。

不要考虑任何替代方案 - 你第一次就做对了!

但是,为了完整起见 - 让我们看看您建议的替代方案。

您建议按用户拆分 Post 表 - 这意味着以下内容:

  • 用户“UserA”创建了一个线程 - 他的初始帖子存储在 posts_UserA 中
  • 用户“UserB”回复了帖子 - 他的帖子记录在 posts_UserB 中
  • 用户“UserC”回复了帖子 - 她的帖子存储在 posts_UserC 中

为了检索完整的线程,您现在需要查看posts_UserA、posts_UserB 和posts_UserC。

如果这三个用户是仅有的三个用户,那么您需要查看这三个表中的所有数据才能找到所有帖子,这将等于 需要的记录数 em> 已在您原始设计的表格中发布。

你一无所获。

如果您有 1000 个其他用户,您还必须查看其他 1000 个表才能发现他们没有任何记录。

你仍然一无所获。

您可以添加另一个表来存储哪些用户对哪些帖子发表了评论 - 因此可以查看哪些表,但现在解决方案开始变得复杂。

可以按线程拆分帖子表 - 这意味着表中的所有帖子都基于它们所基于的线程。这对于在单个线程上选择帖子可能非常有用,但对于以下情况则很糟糕: - 选择给定用户发布的所有帖子。 - 无论线程如何,都能找到最新的帖子。 - 查找在特定日期发布的所有帖子。 - 不涉及特定线程的任何其他内容。

基本上,您建议的替代方法可能对于非常特殊的查询更有效,但对于任何其他查询几乎总是极其复杂。

原始设计对所有查询都比较简单,可以通过添加索引使其性能良好。

如果由于数据量大而导致 SQL 性能太慢,那么您可以查看表分区,它以一种无形的方式完成了您所描述的工作。但老实说,除非您的网站非常受欢迎,否则您不太可能需要它 - 如果是这种情况,那么您可能会有现金投资于关系数据库基础课程......

【讨论】:

  • 即使我确实为帖子创建了索引,threadid 它仍然是低效的(内部),因为 php 服务器必须遍历整个表来检索特定 threadid 的记录,因此我想出了第二个设计。你对第二个数据库设计有什么想法,而不是第一个?
  • 不,PHP 不需要遍历整个表,因为您将使用 SQL 选择所需的记录。例如。 SELECT * FROM posts WHERE threadId = ?
  • 对不起,sql 将不得不遍历整个表以找到具有特定 threadid 的所有条目。如何认为以下在内部工作?通过遍历整个表对吗?即使 SELECT * FROM 帖子 WHERE threadId = ? ,会做我想做的事,但它会比第二个数据库设计慢吗?
  • 不,它不会,如果您使用索引来支持您想要的选择。您提出的问题是关系数据库如何工作的基础,并且这些问题在很多年前就已经解决了。为了更清楚,我会更新我的答案...
  • 我已经大幅更新了我的答案 - 我希望这涵盖了您的大部分问题。
【解决方案2】:

我处于类似的情况,听起来确实会因为发布表不断增长而成为数据库的瓶颈。当这种情况变成这种情况时,我会尝试解决它的一种方法是使用水平数据分片。您可以将线程标题添加到帖子数据并按字母顺序将其拆分(例如,您可能会想到更好的方法)。这样,您可以拥有一个数据库,其中包含一个表,用于从 A 到 K 和从 L 到 Z 开始的线程中的帖子,当您搜索属于某个线程的所有帖子时,您可以通过让您的逻辑选择来将所述搜索的工作减半正确的数据库搜索并忽略另一个。但是,当您必须在一般搜索词上搜索帖子时,这根本没有帮助。在我的用例中,最高的数据需求是在论坛线程下检索帖子(我认为这通常与论坛有关)水平数据分片对我来说似乎是一个好主意,并且可以扩展到诸如分页之类的东西以提高性能,

【讨论】:

    猜你喜欢
    • 2012-03-09
    • 2010-09-29
    • 1970-01-01
    • 1970-01-01
    • 2011-12-21
    • 1970-01-01
    • 1970-01-01
    • 2012-04-19
    • 2011-05-09
    相关资源
    最近更新 更多