为什么订阅会 100% 加载处理器

Why do subscriptions load the processor 100%

我通过以下方式从 hasura 获取统计数据

前端(nuxt.js)

<script>
import subUsers from '~/apollo/subscriptions/stats/users'
import users from '~/apollo/queries/stats/users'

export default {
  apollo: {
    users: {
      query: users,
      update: data => data.users_aggregate.aggregate.count,
      subscribeToMore: {
        document: subUsers,
        updateQuery: (previousResult, { subscriptionData }) => {
          return {
            users_aggregate: subscriptionData.data.users_aggregate
          }
        }
      }
    }
  }
}
</script>

哈苏拉:

query users {
  users_aggregate {
    aggregate {
      count
    }
  }
}
subscription users {
  users_aggregate {
    aggregate {
      count
    }
  }
}

在 table user 我有大约 500,000 个用户

并且由于订阅,我已 100% 加载所有处理器内核

Hasura 通过基于时间间隔的轮询实现订阅,这基本上意味着您的查询是每 1 秒 运行。您可以在此处阅读有关架构决策的详细信息:

由于您运行每秒对 50 万行进行聚合查询,那么...

那你能做什么? Postgres 恰好为您提供了解决方案,它被称为物化视图。您可以根据所需的聚合创建一个这样的视图,然后告诉 PG 仅在有意义时更新它。所以在你的情况下,你会在每次添加或删除用户时更新它。然后你订阅你的视图,它的成本基本上是零(因为视图会记住查询结果直到更新)。

您希望订阅查询能够非常快速地工作。使其快速的一种方法是在每次更改后缓存计数本身。您可以在您的用户 table 上使用触发器来实现这一点。缓存的值存储在 user_aggregate table 中,您现在可以在其上订阅。例如:

drop table if exists user_aggregate;
drop trigger if exists user_aggregate_trigger on user_info;
drop function if exists user_aggregate();

create table user_aggregate (
    id int not null primary key,
    user_aggregate int not null
);

create or replace function user_aggregate()
returns trigger
as $$
begin
    insert into user_aggregate (id, user_aggregate)
    values (1, (select count(id) from user_info))
    on conflict (id) do
    update set user_aggregate = excluded.user_aggregate;
    return new;
end
$$ language plpgsql;

create trigger user_aggregate_trigger
after insert or delete on user_info
for each row execute procedure user_aggregate();

insert into user_aggregate (id, user_aggregate)
values (1, (select count(id) from user_info))
on conflict (id) do
update set user_aggregate = excluded.user_aggregate;

如果您批量 inserting/deleting 很多用户,您可能想要禁用触发器。