4

使用 PHP 和 MySQL,我有一个正在尝试构建的论坛系统。我想知道的是,我如何设置它,以便当用户阅读论坛条目时,无论他们在哪个论坛,它都显示为该用户已阅读,直到其他人在其上发帖。

目前,对于每个线程,我都有一个带有 PostID 的表,并且有发布它的用户 ID、链接到它的 ThreadID、实际的帖子(作为文本),然后是发布的日期/时间。

对于每个论坛的帖子列表,有threadID(主键)、ThreadName、所属的ForumID、NumPosts、NumViews、LastPostDateTime、CreateDateTime。有什么帮助吗?

4

7 回答 7

8

传统的解决方案是连接表,类似于:

CREATE TABLE topicviews (
    userid INTEGER NOT NULL,
    topicid INTEGER NOT NULL,
    lastread TIMESTAMP NOT NULL,
    PRIMARY KEY (userid, topicid),
    FOREIGN KEY (userid) REFERENCES users(id),
    FOREIGN KEY (topicid) REFERENCES topics(id)
);

每次阅读主题时都会更新 lastread。显示主题列表时,如果topics.lastupdated > topicviews.lastread,则有新帖子。

传统的解决方案是垃圾,会杀死你的数据库!不要这样做!

第一个问题是,对每个主题视图的写入很快就会使数据库服务器在繁忙的论坛上陷入瘫痪,尤其是在只有表级锁的 MyISAM 表上。(不要使用 MyISAM 表,对除全文搜索之外的所有内容都使用 InnoDB)。

您可以稍微改善这种情况,只需在主题中实际读取新消息时写完 lastread 时间。如果 topic.lastupdated < topicviews.lastread 更新该值,您将一无所获。即便如此,在一个使用率很高的论坛上,这可能是一个负担。

第二个问题是组合爆炸。每个主题每个用户一行很快加起来:只有一千个用户和一千个主题,您可能有一百万个主题视图行要存储!

您可以通过限制为每个用户记住的主题数量来稍微改善这种情况。例如,您可以在视图表超过某个年龄时从视图表中删除任何主题,并假设所有旧主题都已“阅读”。这通常需要在后台完成清理任务。

其他不太密集的方法包括:

  • 每个论坛只存储一个 lastread time
  • 仅在整个站点中为每个用户存储一个 lastvisit 时间,这将显示为“新”仅自用户上次访问(会话)以来更新的内容
  • 根本不存储任何 lastread 信息,但在主题的 URL 本身中包含最后更新时间。如果用户的浏览器最近看到过该主题,它会记住该 URL 并将其标记为已访问。然后,您可以使用 CSS 将访问的链接设置为“不包含新消息的主题”。
于 2009-02-05T02:31:49.477 回答
3

当用户读取该线程时,可能存储在另一个表 UserID、threadID、LastReadDateTime 中。

if (LastPostDateTime > LastReadDateTime) you got an unread post.

可悲的是,您的开销很大,每次读取都会写入。

于 2009-02-05T02:13:46.750 回答
2

这里的一般想法是正确的,但他们忽略了一些可扩展性问题的明显解决方案。

@bobince:第二个问题是组合爆炸。每个主题每个用户一行很快加起来:只有一千个用户和一千个主题,您可能有一百万个主题视图行要存储!

如果有人从未查看过该线程,则无需在“topicviews”表中存储记录。如果返回 null 或 last_read 时间 < last_post 时间,您只需将主题显示为有未读帖子。这可能会将“百万”行减少一个数量级。

@gortok:有很多方法可以做到这一点,但随着用户访问网站,每一种方法都会成倍增长。

在这种情况下,您在发布 n 个帖子或 n 周后归档一个论坛,并在锁定时清理“topicviews”表。

我的第一个建议很明显,没有缺点。我的第二个降低了存档主题的可用性,但对于一个快速的论坛来说,这是一个很小的代价。缓慢的论坛阅读和发布都很痛苦。

但老实说?您可能不需要担心可伸缩性。即使是一百万行也不是那么多。

于 2009-02-05T04:04:17.560 回答
1

没有简单的方法可以做到这一点。有很多方法可以做到这一点,但随着用户访问网站,每一种方法都会成倍增长。你能做的最好的并且仍然保持性能是有一个时间戳,并将自上次访问以来已更新的所有论坛标记为“未读”。

于 2009-02-05T02:07:39.760 回答
1

您可以只使用用户浏览器的功能并将链接中的最后一个 postid 附加到线程,如下所示:“thread.php?id=12&lastpost=122”

通过在 CSS 中使用 a:visited,您可以显示用户已经阅读的帖子与他未阅读的帖子不同。

于 2009-02-06T13:18:52.007 回答
0

Bobince 有很多好的建议。另外一些潜在的优化:

写下新的“这是新的吗?” 信息到 memcached 和 MySQL“ARCHIVE”表。批处理作业可以更新“真实”表。

对于每个用户,保留一个“在 $date 之前读取的所有内容”标志(当单击“标记所有已读”时)。

当发布新内容时,清除所有“它已被读取”标志——这会减少“标志”的数量,并且表格可以只是(topic_id,user_id)——没有时间戳。

于 2009-02-05T09:21:01.177 回答
0

功能用户浏览器的使用,并在帖子的链接中添加最后一个帖子 ID。在 CSS 中使用 a:visited 后,您可以显示所有未由用户阅读的线程。

于 2017-02-06T13:30:48.290 回答