索引/主键序号的SQL查询

时间:2012-07-24 03:57:15

标签: sql postgresql ordinal

在我们的在线竞赛系统中,有一个频繁更改的表standings,其中包含整数列(user_id, score)。两者都使用唯一约束进行索引。需要两种查询:

  1. 如果表中没有score,则返回分数在插入时将占据的1位置。
  2. 在表格中给出user_id,返回相应分数的位置。
  3. 在这两种情况下,位置都与分数上升有关:小于当前表中所有分数的新分数将具有位置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无法解决问题,确认这是此问题的下一个最佳结果。

1 个答案:

答案 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子句,它也可以提高性能,就像你的情况一样。