让我们假设我们有一个文章表:
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
);
我现在要做的是告知用户用户上次看到后已更新的文章。通过Web界面访问整个系统。每当请求页面时,系统应检查是否应通知用户有关更新的文章(类似于此处SO页面顶部的通知栏)。
如果上面的两个表都包含数千万行,那么这种功能的最佳和最有效的实现是什么?
可以像这样做一个简单的连接:
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
谓词。
另一种方法更难,但在我的情况下可能更好。它涉及通过通知列扩展书签表:
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的低效变体,因为它涉及保存通知。
当两个表包含数百万行时,哪种方法最有效?你有其他方法可能更好吗?
答案 0 :(得分:1)
我当然会选择解决方案一,确保文章上有索引(article,last_update)。
答案 1 :(得分:1)
归一化理论将您直接带到解决方案#1。您可能想问一下,如何让我的服务器有效地执行此查询,而不是要求哪个设计更快,而不是在我的bog标准BCNF表中。 :-)
如果无法让您的服务器足够快地执行您的查询(无论您的足够的值),您需要更快的服务器。为什么?因为性能只会随着用户和行的添加而降低。发明了规范化以最小化更新和更新异常。使用它对您有利,或者花费您几个小时的时间和系统中难以检测的错误。
答案 2 :(得分:0)
我看到第三种解决方案,让事情变得更有趣。 ;-)它是两种解决方案的混合物。我会假设白天或晚上有一段时间,系统上的使用很少,并且每天/每晚都会标记所有新标记。
仅此一项就会延迟“为您提供新文章更新信息”。一天不是你想要的。但是我会存储一个额外的列“今天更新”(枚举“是”,“否”或tinyint),在文章更新时设置为“是”,并在每晚更新运行时重置为“否”。
然后显示所有书签的“已更改”,标记为“已更改”(来自夜间cron),另外使用选择版本1添加信息,但仅限于今天更改的文章。
大多数文章可能每天都没有更新,所以你应该获胜。
当然我会批准测量答案,但你需要做很多假设来制定一个好的基准。