0

让我们假设我们有一个文章表:

CREATE TABLE articles
(
    id      integer PRIMARY KEY,

    last_update timestamp NOT NULL,

    ...
);

用户可以为文章添加书签:

CREATE TABLE bookmarks
(

    user        integer NOT NULL REFERENCES users(id),
    article     integer NOT NULL REFERENCES articles(id),

    PRIMARY KEY(user, article),

    last_seen   timestamp NOT NULL

);

要实现的功能

我现在要做的是通知用户在用户最后一次看到它们之后已经更新的文章。通过网络界面访问整个系统。每当请求页面时,系统应检查是否应通知用户有关更新的文章(类似于此处页面顶部的通知栏)。

问题

鉴于上面的两个表都包含数千万行,这种特性的最佳和最有效的实现是什么?

我的解决方案#1

可以像这样进行简单的连接:

SELECT ... FROM articles, bookmarks WHERE bookmarks.user = 1234
AND bookmarks.article = articles.article AND last_seen < last_update;

但是,我担心如果用户有很多书签文章(这可能比您想象的更频繁地发生),那么执行此 JOIN 可能会很昂贵,特别是如果数据库(在我的情况下为 PostgreSQL)必须遍历主键上的索引为articles每篇添加书签的文章。此外,last_seen < last_update只能在访问磁盘上的行后检查谓词。

我的解决方案#2

另一种方法更困难,但在我的情况下可能会更好。它涉及通过通知列扩展书签表:

CREATE TABLE bookmarks
(

    user        integer NOT NULL REFERENCES users(id),
    article     integer NOT NULL REFERENCES articles(id),

    PRIMARY KEY(user, article),

    last_seen   timestamp NOT NULL,

    notify      boolean NOT NULL DEFAULT false

);

CREATE INDEX bookmark_article_idx ON bookmarks (article);

每当一篇文章被更新时,更新操作应该为每个收藏了这篇文章的用户触发设置 notify 为 true。想到的最大缺点是,如果一篇文章被添加了很多书签,那么将很多行的 notify 设置为 true 可能会很昂贵。优点可能是检查通知很简单:

SELECT article FROM bookmarks WHERE user = 1234 AND notify = true;

最后的想法

我认为如果页面浏览次数(以及系统检查通知的次数)超过文章更新次数,第二种方法会更有效。但是,情况可能并非总是如此。可能有些用户有很多书签文章,每月只登录一次几分钟,而其他用户几乎每分钟检查一次更新。

还有第三种方法涉及通知表,一旦文章更新,系统就会在其中为每个用户插入通知。但是,我认为方法 #2 的一种低效变体,因为它涉及保存通知。

当两个表都包含数百万行时,哪种方法最有效?你有另一种可能更好的方法吗?

4

3 回答 3

1

我肯定会选择解决方案一,确保文章在 (article,last_update) 上有一个索引。

于 2013-05-12T11:50:33.570 回答
1

归一化理论将您直接带到解决方案 #1。与其问哪种设计更快,您可能想问,在给定我的沼泽标准 BCNF 表的情况下,如何让我的服务器有效地执行此查询。:-)

如果您的服务器无法足够快地执行您的查询(对于您的情况而言足够的任何值),您需要一个更快的服务器。为什么?因为性能只会随着用户和行的添加而降低。规范化被发明来最小化更新和更新异常。将其用于您的优势,或者以您的时间和系统中难以检测的错误为代价付出代价。

于 2013-05-13T05:08:30.100 回答
0

我看到了第三种解决方案,让事情变得更有趣。;-) 它是两种解决方案的混合物。我会假设在白天或晚上的某个时间,系统上的使用量很少,并每天/每晚运行一次以标记所有新的书签。

仅此一项就会延迟“为您更新新文章”的信息!一天,这不是你想要的。但我会存储一个附加列“今天更新”(枚举“是”、“否”或 tinyint),在文章更新时设置为“是”,并在每晚更新运行时重置为“否”。

然后显示带有“已更改”标记的所有书签的“已更改”(来自每晚的 cron),并另外添加从版本 1 中选择的信息,但仅限于今天已更改的文章。

可能大多数文章都不会每天更新,所以你应该赢。

当然我会批准测量答案,但你需要很多假设才能做出好的基准。

于 2013-05-12T12:43:26.500 回答