Slow Counting/ja

From PostgreSQL wiki
Jump to navigationJump to search

以下の記事は9.2より前のPostgreSQLバージョンにのみ適用されることに注意してください。今はインデックスオンリースキャンが実装されています。

以下の例のようなテーブル内の全行数を数えることは、PostgreSQLの性能が遅いことが分かっている操作の1つです。

SELECT COUNT(*) FROM table

これが低速となる理由はPostgreSQLのMVCC実装に関連します。 複数のトランザクションが異なるデータ状態を参照することができることは、"COUNT(*)"のためにテーブル全体に渡るデータをまとめる簡単な方法があり得ないことを意味します。 別の見方をすると、PostgreSQLは必ずすべての行をたどります。 これは通常、テーブル内の全行に関する情報をシーケンシャルスキャンを使用して読み取ることになります。 問い合わせがどのように進んでいるかを確認する優れた方法はEXPLAIN ANALYZEを使用することです。

postgres=# EXPLAIN ANALYZE SELECT COUNT(*) FROM accounts;
                                                      QUERY PLAN                                                       
-----------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=4499.00..4499.01 rows=1 width=0) (actual time=465.588..465.591 rows=1 loops=1)
   ->  Seq Scan on accounts  (cost=0.00..4249.00 rows=100000 width=0) (actual time=0.011..239.212 rows=100000 loops=1)
 Total runtime: 465.642 ms
(3 rows)

悲観的にならざるを得ないのがこの厳密な集約構文だけであることがわかることには価値があります。もし次のように"WHERE"句があったとすると、PostgreSQLは限定されたフィールドに対して利用可能なインデックスを利用して、数えなければならないレコードの行数を制限します。

SELECT COUNT(*) FROM table WHERE status = 'something'

これによりこうした問い合わせは大きく高速化されます。PostgreSQLはまだ、行が存在するかどうかを検証するために結果行を読み取る必要があります。他のデータベースシステムでは、こうした状況ではインデックスを参照する必要があるだけかもしれません。

行数の推定

おおよその行数だけが必要である場合、PostgreSQLには1つの代替方法があります。 これは以下のようにpg_classカタログのreltuplesフィールドを使用することです。

pgbench=# select reltuples from pg_class where relname='tellers';
 reltuples 
-----------
       250

この前提となるのは、統計情報が最新情報を維持できるほど十分にテーブルに対してANALYZEを実行していることです。

他にもトリガを基にした機構を使用してテーブル内の行数を数える方法がよく使われます。 これらの技法の片方、または両方の説明は以下にあります。