在我们的在线竞赛系统中,有一个频繁更改的表standings
,其中包含整数列(user_id, score)
。两者都使用唯一约束进行索引。需要两种查询:
score
,则返回分数在插入时将占据的1位置。 user_id
,返回相应分数的位置。在这两种情况下,位置都与分数上升有关:小于当前表中所有分数的新分数将具有位置1.
这是困难的部分:我们可能买不起桌面扫描。该表可能有多达1000万条记录,我们需要每秒至少处理40个查询。
如何在PostgreSQL中执行此操作?
我在Berkeley DB中有一个非SQL解决方案,它使用其启用逻辑记录号的B树。它很容易具有足够好的性能。但我们希望通过使用PostgreSQL查询重新实现来摆脱BDB。我试过了明显的
select 1+count(*) from standings where score < ? limit 1;
这会导致表扫描。
我希望答案是“不可能”,因为BDB的逻辑记录号设施需要为每次编辑锁定整个B-Tree。要获得O(log N)性能,它依赖于每个节点中的叶子计数。通往root的路径中的所有这些计数必须随着每次编辑而改变;因此,锁定。这种锁定违反了PostgreSQL的设计原则,可能还有任何多用户数据库。
因此,如果使用PostgreSQL无法解决问题,确认这是此问题的下一个最佳结果。
答案 0 :(得分:2)
使用常规表,你可以在PostgreSQL 9.1中做。 count()
导致表扫描,因为索引没有可见性信息。为了验证在此期间不删除行,PostgreSQL必须访问该表。
如果表是只读的(或很少更新),则可以向表中添加行号。然后查询如下:
SELECT rownumber+1
FROM standings
WHERE score < ?
ORDER BY score DESC
LIMIT 1;
使用索引:
CREATE INDEX standings_score_idx ON standings (score DESC);
几乎可以立即获得结果。 但是,出于显而易见的原因,不是具有写入负载的表的选项。所以不适合你。
好消息:即将发布的 PostgreSQL 9.2 的主要新功能之一适合您:“覆盖索引”或“仅限索引”扫描强>”。我引用9.2发行说明here:
允许查询仅从索引检索数据,从而避免堆访问 (Robert Haas,Ibrar Ahmed,Heikki Linnakangas,Tom Lane)
这通常称为“仅索引扫描”或“覆盖索引”。这是 对于具有完全可见元组的堆页,可以使用 由能见度图报告。可见性图是防撞安全的 作为实现此功能的必要部分。
This blog post by Robert Haas详细说明了这会如何影响计算效果。即使使用WHERE
子句,它也可以提高性能,就像你的情况一样。