【问题标题】:SQL query for index/primary key ordinal索引/主键序数的 SQL 查询
【发布时间】:2012-07-22 08:40:41
【问题描述】:

在我们的在线竞赛系统中,有一个经常变化的表格standings,其中整数列(user_id, score)。两者都使用唯一约束进行索引。需要两种查询:

  1. 如果score 不在表中,则返回从 1 开始的位置,如果插入该分数将占据该位置。
  2. 给定表格中的user_id,返回对应分数的位置。

在这两种情况下,位置都是相对于分数升序的:比表中当前所有分数小的新分数将具有位置 1。

这是困难的部分:我们可能负担不起表扫描。该表可能有多达 1000 万条记录,我们需要每秒处理至少 40 个查询。

如何在 PostgreSQL 中做到这一点?

我在 Berkeley DB 中有一个非 SQL 解决方案,它使用支持逻辑记录号的 B 树。它很容易具有足够好的性能。但是我们想通过使用 PostgreSQL 查询重新实现来摆脱 BDB。我已经尝试了明显的

select 1+count(*) from standings where score < ? limit 1;

这会导致表扫描。

我希望答案是“不可能”,因为 BDB 的逻辑记录编号功能需要为每次编辑锁定整个 B 树。为了获得 O(log N) 的性能,它依赖于每个节点中的叶子数。 root 路径中的所有这些计数都必须随着每次编辑而改变;因此,锁定。这种锁定违反了 PostgreSQL 的设计原则,并且可能违反了任何多用户数据库。

因此,如果问题不能用 PostgreSQL 解决,那么确认这一点是这个问题的下一个最佳结果。

【问题讨论】:

  • 该表是只读的吗?还是只在特定时间/很少写?如果是,你能负担得起添加一列position吗?
  • 不,随着人们添加参赛作品,它会经常变化。

标签: sql postgresql ordinal


【解决方案1】:

使用常规表,在 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 的主要新功能之一正好适合您:“Covering index”或“index-only扫描”。我引用 9.2 发行说明here

允许查询仅从索引中检索数据,避免堆访问 (罗伯特·哈斯、伊布拉尔·艾哈迈德、海基·林纳坎加斯、汤姆·莱恩)

这通常称为“仅索引扫描”或“覆盖索引”。这是 对于具有完全可见的元组的堆页面是可能的,如 由可见性地图报告。能见度地图是碰撞安全的 作为实现此功能的必要部分。

This blog post by Robert Haas 提供了更多详细信息,这将如何影响计数性能。即使使用 WHERE 子句,它也有助于提高性能,就像您的情况一样。

【讨论】:

  • 谢谢!现在,如果 Heroku(目标)将只实现 9.2!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-02
  • 2018-12-01
  • 2012-05-01
  • 1970-01-01
  • 2012-09-04
  • 2011-04-06
  • 1970-01-01
相关资源
最近更新 更多