我正在尝试为以下问题找到最佳解决方案:需要设计一个数据库(基于postgres),其中包含触发器和计数器系统,它将形成一个有效查询、更新和存储信息的系统'显示在页面上的每篇文章(或博客条目,或类似的东西)中存在多少未读评论'。
每个出现的解决方案都有一些严重的缺点,无论是在查询、存储还是更新部分。即它需要太多的存储空间,或者太多的更新,或者太昂贵的查询。
你的经历呢?也许对于这类问题已经形成了很好的解决方案?
我正在尝试为以下问题找到最佳解决方案:需要设计一个数据库(基于postgres),其中包含触发器和计数器系统,它将形成一个有效查询、更新和存储信息的系统'显示在页面上的每篇文章(或博客条目,或类似的东西)中存在多少未读评论'。
每个出现的解决方案都有一些严重的缺点,无论是在查询、存储还是更新部分。即它需要太多的存储空间,或者太多的更新,或者太昂贵的查询。
你的经历呢?也许对于这类问题已经形成了很好的解决方案?
我会尽可能简单地保持模式,因此查询将尽可能简单。这通常也具有最低的存储要求。当然,设置索引来支持这个查询。
下一步:测量性能!“衡量就是知道。” 响应时间是多少?服务器的负载是多少?只要性能是可以接受的,保持模式和查询简单。如果不是绝对必要,请不要牺牲可维护性:您的继任者稍后会感谢您。
如果性能确实是个问题,请查看您用于应用程序的框架的缓存功能。不执行查询总是比执行优化查询快。
如果你真的没有在你的资源范围内取得成功,也许你必须调整用户体验。也许存储最后一次访问线程的日期就足够了。
我不相信典型的标准化方法会给您带来低效的查询。假设您有一个带有 PK 的表article_comments
和(article_id, comment_id)
另一个comments_seen_by_user
带有 PK的表(user_id, article_id, comment_id)
。对于页面上列出的每篇文章,您需要做的就是:
SELECT count(*) FROM article_comments ac
WHERE article_id = ? -- Parameter
AND NOT EXISTS (
SELECT 1 FROM comments_seen_by_user csbu
WHERE csbu.user_id = ? -- Parameter
AND csbu.article_id = ac.article_id
AND csbu.comment_id = ac.comment_id
)
如果您在一页上显示 20 篇文章,您将运行上述查询 20 次,每次运行将使用一个索引从 中提取 10-20 行article_comments
,而子查询测试只是对 的另一个索引扫描comments_seen_by_user
,所以全部在您可能需要执行 20 * (20 * 2) = 800 次索引查找来显示给定页面。这对现代数据库来说并不费力。而且我可能忽略了 PostgreSQL 可能找到的更好的查询计划。
你有没有试过这个,发现性能不足?如果是这样,我的第一个猜测是你有一段时间没有VACUUM
编辑了。否则,我对每页文章数量或每篇文章评论数的估计一定是错误的——在这种情况下,请更新更多详细信息。
我将第二个 j_random_hacker 的答案,只是我会避免将 article_id 存储在 comments_seen_by_user 表中,因为每条评论的 comment_id 应该是全局唯一的。此外,PostgreSQL 中的 3 维(和 2 维程度较低)索引仍然很慢,因此请尽量避免使用它们。
围绕 user_id、comment_id 值的表没有真正好的方法来存储有关阅读评论的信息,只需确保它具有唯一索引即可。这样的表中几千万行对PostgreSQL来说完全没有问题,只要它可以将索引保留在内存中。您可以通过查询系统表来跟踪索引大小(磁盘上 8KB 页面的数量):
select relname,relpages from pg_class where relname='comments_seen_by_user_pkey';
我同意采用标准化方法,看看是否可行。通常我应该。但是,您也可以在“评论”表上使用一些 INSERT 触发器,它会更新基本(即文章)表中的评论计数器。这取决于该网站的使用情况:如果大多数人阅读评论(与添加评论相比),则基于触发器的方法的开销应该会迅速摊销。如果它是一个具有高评论负载的网站,这可能会影响性能。
当您有一些合理的使用配置文件时,我会选择一个简单的规范化表结构并在以后添加其他优化。