【问题标题】:Counter cache column in PostgreSQLPostgreSQL 中的计数器缓存列
【发布时间】:2014-09-12 06:32:09
【问题描述】:

在我的数据库中,我有 taskscomments 表。每个任务都有很多 cmets。

我想创建将由 PostgreSQL 自动更新的 tasks.comments_count 列,因此我可以在选择所有任务时在 O(1) 时间内获得 comments_count(并按其排序/过滤)。

我知道有一些特定于语言的解决方案,例如 ActiveRecord 的 counter cache,但我不想使用它们(我发现它们很脆弱)。我希望 PostgreSQL 处理此类计数器缓存。

我也知道 PostgreSQL 支持触发器,但是它们很难编写和使用(不是一个可靠的解决方案)

理想情况下,它应该是一个 PostgreSQL 扩展或一些我不知道的原生特性。

此类计数器的延迟计算将是一个很大的好处。

【问题讨论】:

  • 为什么不只计算查询中每个任务的 cmets 数,而不必在数据库表中维护该数字?另一种选择可能是使用触发器并使用添加或删除的每个新评论更新tasks.comments_count
  • @ydaetskcoR 我提供了一个简单易懂的例子。在实际应用中,有多个计数器,并且需要对它们进行高级排序/过滤/分组,因此在查询中计算关联记录并不是一个高效的解决方案。假设将计数器缓存存储在表中是客户端要求。
  • 我之前没有遇到过“计数器缓存”,但是,如前所述,如果您想将性能影响转移到写入而不是比读。它也是一种便携式解决方案。
  • “[触发器]很难编写和使用(不是可靠的解决方案)”是什么意思?触发器并不比其他任何东西更难编写或使用。 “懒惰的计算”是什么意思?

标签: performance postgresql caching


【解决方案1】:

如果您希望 Postgres 在插入/更新/删除的基础上自动执行某些操作 - 即,如果您希望此操作触发其他操作 - 那么您需要编写一个触发器。

这很简单。足够简单,我怀疑有人会费心创建扩展(更不用说语言功能)来省去麻烦了。而且它肯定比 ActiveRecord 在后台执行的任何操作都更简单(并且正如您所指出的,更安全)。

通常只需要这样的东西(我还没有测试过,所以你可能想这样做......):

CREATE FUNCTION maintain_comment_count_trg() RETURNS TRIGGER AS
$$
BEGIN
  IF TG_OP IN ('UPDATE', 'DELETE') THEN
    UPDATE tasks SET comment_count = comment_count - 1 WHERE id = old.task_id;
  END IF;
  IF TG_OP IN ('INSERT', 'UPDATE') THEN
    UPDATE tasks SET comment_count = comment_count + 1 WHERE id = new.task_id;
  END IF;
  RETURN NULL;
END
$$
LANGUAGE plpgsql;

CREATE TRIGGER maintain_comment_count
AFTER INSERT OR UPDATE OF task_id OR DELETE ON comments
FOR EACH ROW
EXECUTE PROCEDURE maintain_comment_count_trg();

如果您希望它是密封的,您需要在comments 上为TRUNCATEs 附加一个触发器;是否值得麻烦取决于您。

要处理对被引用的tasks.id 值的更新(通过延迟约束或ON UPDATE 操作),还有更多工作要做,但这种情况并不常见。

如果您的客户端库/ORM 足够天真,可以通过每个 UPDATE 语句中的每个字段发送,您可能需要一个单独的 UPDATE 触发器,该触发器仅在值实际更改时触发:

CREATE TRIGGER maintain_comment_count_update
AFTER UPDATE OF task_id ON comments
FOR EACH ROW
WHEN (old.task_id IS DISTINCT FROM new.task_id)
EXECUTE PROCEDURE maintain_comment_count_trg();

【讨论】:

  • 你好尼克,你能解释一下WHEN (old.task_id IS DISTINCT FROM new.task_id)的必要性
  • @lessless:我已经更新了我的答案以更好地解释这一点。
  • 谢谢!将 CC 下推到数据库级别是一个非常非常明智的选择 :)
  • 不起作用:(它说函数在没有RETURN的情况下结束了
  • @NickBarnes 它需要一个DECLARE 和一个RETURN
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多