【问题标题】:Efficiently calculate number of currently active users有效计算当前活跃用户数
【发布时间】:2015-02-25 04:36:42
【问题描述】:

我目前正忙于弄清楚如何有效地计算应用程序上任何给定“页面”上的活跃用户数量。我正在使用 PostgreSQL 并且有一个这样的表:

CREATE TABLE user_is_viewing_page (
  user_id BIGINT,
  page_id BIGINT,
  timestamp TIMESTAMP
);

每个用户将每隔 10 秒左右使用他们正在查看的页面的 id 向服务器发送 POST,并将新行插入到数据库中。

我必须计算任何给定页面上“活跃”用户数量的当前查询是:

SELECT COUNT(DISTINCT user_id)
FROM user_is_viewing_page WHERE page_id = 1 
                          AND timestamp > CURRENT_TIMESTAMP - INTERVAL '10 seconds';

我想知道使用此查询最有效的方法是什么。请记住,我需要经常访问此计数(每页每 5-10 秒)。

  • 是否应该使用查询创建实体化视图? (知道我必须经常访问它)
  • 是否应该在每次需要时自行运行查询?
  • 为我的表建立索引以实现快速查找的最佳方法是什么?

【问题讨论】:

  • 为什么需要经常访问它?通常,您只关心汇总统计信息。如果您通过范围表“批量处理”查询(即,使用 CTE 声明包含 (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP - 10 SECONDS), (CURRENT_TIMESTAMP - 10 SECONDS, CURRENT_TIMESTAMP - 20 SECONDS)... 的“临时”表,然后您也可以按该表进行分组)等,您可能会获得更好的性能。
  • 我需要提供当前正在查看页面的用户数量的伪实时更新。所以不幸的是,像这样简单地运行一个查询是行不通的。

标签: sql performance postgresql indexing postgresql-performance


【解决方案1】:

物化视图不会有帮助,因为您的查询需要基于最新数据,并且您必须像查询一样频繁地刷新 MV。

基于触发器的解决方案 将是另一种选择:使用当前每页的计数来更新辅助表。但我预计(您的许多)写入操作的额外成本将高于读取操作的收益。所以我也会排除这个。

当您使用一张大桌子时,我建议您使用partial index

CREATE INDEX foo ON user_is_viewing_page (page_id, timestamp)
WHERE timestamp > '2014-12-29 23:30:00'::timestamp;  -- start with 'now'

查询(大部分是您已经拥有的):

SELECT COUNT(DISTINCT user_id)
FROM   user_is_viewing_page
WHERE  page_id = 1 
AND    timestamp > LOCALTIMESTAMP - INTERVAL '10 sec';

CURRENT_TIMESTAMP 也可以。但是LOCALTIMESTAMP 对您的设置更有意义。 Per documentation:

CURRENT_TIMECURRENT_TIMESTAMP 传递带有时区的值; LOCALTIMELOCALTIMESTAMP 提供没有时区的值。

查找部分索引本身的成本与使用完整索引的成本基本相同。但是由于您的表应该是,部分索引应该比完整索引小很多,这将更适合并保留在RAM中并且通常更快。如果您有足够多的 RAM,请将性能与没有WHERE 条件的简单、大的完整索引进行比较。

部分索引的优势随着时间的推移而明显恶化。以您选择的时间间隔在WHERE 条件中创建一个具有更新时间戳的新索引,然后删除旧索引。查询将立即启动新的(较小的)索引,因此可以轻松删除旧的索引。这些相关答案中概述了可能的自动化方法,并提供了更多解释:

您可能需要将索引的确切WHERE 条件添加到您的查询中(尽管看起来是多余的),以使查询规划器相信使用部分索引是安全的。尤其是 prepared statements(包括 plpgsql 函数中的所有语句),其中要比较的实际时间戳是参数化的,否则 Postgres 不能将部分索引用于 generic query plan

在上面的示例中,您将在查询中添加 WHERE 条件:

AND timestamp > '2014-12-29 23:30:00'::timestamp -- matches index condition exactly

可以在the linked answer above 中找到更通用的解决方案。

另外:我不会使用“时间戳”作为标识符,因为它是一个基本类型名称。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-28
    • 1970-01-01
    • 2012-05-24
    • 1970-01-01
    • 1970-01-01
    • 2020-07-06
    • 1970-01-01
    • 2021-08-27
    相关资源
    最近更新 更多