在Web应用程序中提供快速“选择计数(*)”功能

时间:2012-10-07 14:50:41

标签: sql postgresql heroku berkeley-db

我正在重新实施一个应用程序来支持国家工程竞赛,将其从本地服务器迁移到云端。

为了告诉团队目前他们的立场,查询的格式为

select 1 + count(*) from team where where score < ?

团队成绩非常动态变化。最多可能有200万个团队,我需要每秒处理至少10个这样的查询。

原始版本通过使用单独的Berkeley DB团队/分数记录获得所需的性能(实际上它已经与1999硬件一起使用)。 Berkeley DB中有一个“记录号”功能,它提供了完全正确的功能,而且速度非常快。

Heroku显然无法支持Berkeley DB。 PostgreSQL,它们的标准数据库,使用完整的表或索引扫描执行select count(*),这太慢了。

有关如何进行的任何想法?我不是和Heroku结合,而是必须转向某种云解决方案。

4 个答案:

答案 0 :(得分:2)

创建排名表并尽可能频繁地更新。包括类别(开放或官方)和分数,这样您就不必在查询时将其加入团队表:

create table "rank" (
    team integer primary key, 
    category integer,
    score integer,
    rank_consolidated integer,
    rank_category integer
);

begin;
truncate table "rank"
;
insert into "rank" (team, category, score, rank_consolidated, rank_category)
select 
    team, category, score,
    rank() over(order by score desc) rank_consolidated,
    rank() over(partition by category order by score desc) rank_category
from team
;
commit
;
select * from "rank" where team = 11;

至于window functions

的确切排名行为

答案 1 :(得分:2)

使用redis将您的团队数据存储在sorted set中。然后ZRANK函数将返回您需要的计数。 Redis的速度非常快,ZRANK函数的预期值为O(log N)。它通过跳过列表实现。

答案 2 :(得分:0)

在分数上设置索引应该避免全表扫描。

答案 3 :(得分:0)

如果读取的内容比它写的要多得多,并且它总是必须是最新的,那么这是触发器维护的摘要表(一种物化视图)的理想工作。

team表上有一个触发器,AFTER EACH INSERT OR UPDATE OR DELETE FOR EACH ROW执行触发器功能,使用新分数更新该团队的team_summary表条目。

team_summary表可以通过简单的直接索引查找来获取,因此它会很快疯狂。由于Pg支持同时读者和编写者,team_summary表将保持响应,即使它经常更新。为了获得最佳效果,您真正需要做的唯一事情是将FILLFACTOR设置为team_summary表中的50,以便HOT可以很好地工作,并确保autovacuum设置为经常运行以传播负载真空I / O流失。

编写触发器应该非常简单。您必须小心编写并发安全触发器,当您通过多个并发连接同时更新同一团队时,该触发器不会中断。类似的东西:

UPDATE team_summary SET score = score + 1 WHERE team_id = NEW.team_id;
SERIALIZABLEREAD COMMITTED隔离下,

应该没问题。见Concurrency control。唯一困难的是,在将新团队的第一行插入team_summary之前,您必须确保在team中插入一个新行,这样您的触发器就不必处理令人惊讶的棘手案例。 team_summary表中可能尚不存在team行。获得upup / merge是正确的。

如果写入速率也非常高,并且您只能每隔几秒钟或几分钟更新一次结果,请使用Clodoaldo的方法。