3

私たちのオンラインコンテストシステムには、standings整数列の頻繁に変更されるテーブルがあります(user_id, score)。どちらも一意の制約でインデックスが付けられます。2種類のクエリが必要です。

  1. テーブルscoreにない場合は、スコアが挿入された場合にスコアが占める1ベースの位置を返します。
  2. 表にauser_idを指定して、対応するスコアの位置を返します。

どちらの場合も、位置はスコアの昇順を基準にしています。現在テーブルにあるすべてのスコアよりも小さい新しいスコアの位置は1になります。

ここが難しい部分です。おそらく、テーブルスキャンを行う余裕はありません。テーブルには最大1,000万のレコードが含まれる可能性があり、1秒あたり少なくとも40のクエリを処理する必要があります。

PostgreSQLでこれを行う方法は?

Berkeley DBには、論理レコード番号が有効なBツリーを使用する非SQLソリューションがあります。簡単に十分なパフォーマンスが得られます。しかし、PostgreSQLクエリを使用して再実装することでBDBを取り除きたいと思います。私は明白なことを試みました

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

これにより、テーブルスキャンが発生します。

BDBの論理レコード番号機能では、編集ごとにBツリー全体をロックする必要があるため、答えは「仕方がない」と思います。O(log N)のパフォーマンスを得るには、各ノードのリーフカウントに依存します。ルートへのパスにあるこれらすべてのカウントは、編集するたびに変更する必要があります。したがって、ロック。このようなロックは、PostgreSQLおよびおそらくマルチユーザーデータベースの設計原則に反します。

したがって、PostgreSQLで問題を解決できない場合は、これを確認することがこの質問の次善の結果です。

4

1 に答える 1

2

通常のテーブルでは、PostgreSQL9.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の主要な新機能の1つは、 「カバーリングインデックス」または「インデックスのみのスキャン」です。ここで9.2リリースノートを引用します:

クエリがインデックスからのみデータを取得できるようにし、ヒープアクセスを回避します(Robert Haas、Ibrar Ahmed、Heikki Linnakangas、Tom Lane)

これは、「インデックスのみのスキャン」または「インデックスのカバー」と呼ばれることがよくあります。これは、可視性マップで報告されているように、すべてが表示されているタプルのみを含むヒープページで可能です。この機能を実装するために必要な部分として、可視性マップがクラッシュセーフになりました。

Robert Haasによるこのブログ投稿には、これがカウントパフォーマンスにどのように影響するかについての詳細があります。WHEREあなたの場合のように、それは節があってもパフォーマンスを助けます。

于 2012-07-24T04:59:12.187 に答える