web-dev-qa-db-ja.com

単純なクエリの実行が非常に遅い

データのレポートを作成しようとしましたが、大きなテーブルでは非常に時間がかかります。

テーブルの構造は次のとおりです。

CREATE TABLE posts
(
  id serial NOT NULL,
  project_id integer,
  moderation character varying(255),
  keyword_id integer,
  author_id integer,
  created_at timestamp without time zone,
  updated_at timestamp without time zone,
  server_id character varying(255),
  social_creation_time integer,
  social_id character varying(255),
  network character varying(255),
  mood character varying(255) DEFAULT NULL::character varying,
  url text,
  source_id integer,
  location character varying(255),
  subject_id integer,
  conversation_id integer,
  CONSTRAINT posts_pkey PRIMARY KEY (id)
);

CREATE INDEX index_posts_on_author_id ON posts (author_id);
CREATE INDEX index_posts_on_keyword_id ON posts (keyword_id);
CREATE INDEX index_posts_on_project_id_and_network_and_social_id 
    ON posts (project_id, network, social_id);
CREATE INDEX index_posts_on_project_id_and_social_creation_time 
    ON posts (project_id, social_creation_time DESC);
CREATE INDEX index_posts_on_server_id ON posts (server_id);
CREATE INDEX index_posts_on_social_id ON posts (social_id);

クエリ:

SELECT date_trunc('hour', timestamp 'Epoch' 
           + (posts.social_creation_time * INTERVAL '1 second')) creating,
       network, 
       count(*) posts 
FROM posts 
WHERE posts.project_id = 7 
   AND (posts.moderation NOT IN ('junk','spam')) 
   AND (posts.social_creation_time BETWEEN 1391716800 AND 1392839999) 
GROUP BY network, creating 
ORDER BY creating

カウントは3940689です。

計画を説明する

 GroupAggregate  (cost=631282.11..671932.05 rows=338750 width=12) (actual time=22576.318..23826.124 rows=1776 loops=1)
   ->  Sort  (cost=631282.11..639750.85 rows=3387494 width=12) (actual time=22576.188..23438.485 rows=3536790 loops=1)
         Sort Key: (date_trunc('hour'::text, ('1970-01-01 00:00:00'::timestamp without time zone + ((social_creation_time)::double precision * '00:00:01'::interval)))), network
         Sort Method: external merge  Disk: 92032kB
         ->  Seq Scan on posts  (cost=0.00..205984.62 rows=3387494 width=12) (actual time=29.542..1954.865 rows=3536790 loops=1)
               Filter: (((moderation)::text <> ALL ('{junk,spam}'::text[])) AND (social_creation_time >= 1391716800) AND (social_creation_time <= 1392839999) AND (project_id = 7))
               Rows Removed by Filter: 404218
 Total runtime: 23842.532 ms
(8 rows)

Time: 23860.876 ms

これはseqスキャンですが、インデックスの使用を強制しても役に立ちません:

GroupAggregate  (cost=815927.00..856583.47 rows=338804 width=12) (actual time=24634.378..25873.754 rows=1778 loops=1)
   ->  Sort  (cost=815927.00..824397.09 rows=3388039 width=12) (actual time=24634.243..25498.578 rows=3537295 loops=1)
         Sort Key: (date_trunc('hour'::text, ('1970-01-01 00:00:00'::timestamp without time zone + ((social_creation_time)::double precision * '00:00:01'::interval)))), network
         Sort Method: external merge  Disk: 92048kB
         ->  Bitmap Heap Scan on posts  (cost=191020.29..390555.96 rows=3388039 width=12) (actual time=4074.171..5685.734 rows=3537295 loops=1)
               Recheck Cond: (project_id = 7)
               Filter: (((moderation)::text <> ALL ('{junk,spam}'::text[])) AND (social_creation_time >= 1391716800) AND (social_creation_time <= 1392839999))
               Rows Removed by Filter: 67925
               ->  Bitmap Index Scan on index_posts_on_project_id_and_network_and_social_id  (cost=0.00..190173.29 rows=3617164 width=0) (actual time=4054.817..4054.817 rows=3605225 loops=1)
                     Index Cond: (project_id = 7)
 Total runtime: 25891.215 ms

テーブルの行の例:

   id   | project_id | moderation | keyword_id | author_id |         created_at         |         updated_at         | server_id | social_creation_time |    social_id    |  network  | mood |                url                 | source_id |      location      | subject_id | conversation_id
---
 204202 |          2 | pending    |            |    125845 | 2014-01-22 15:14:14.786454 | 2014-01-22 15:14:14.786454 | 20620977  |           1390318030 | -64193113_14905 | vkontakte |      | https://vk.com/wall-64193113_14905 |        64 | Россия, Черепаново |            |

**更新**

work_memを設定するのに役立つ実際

私の新しい計画:

HashAggregate  (cost=247145.17..254270.53 rows=356268 width=12) (actual time=2564.201..2564.731 rows=1853 loops=1)
    ->  Seq Scan on posts  (cost=0.00..220425.11 rows=3562675 width=12) (actual time=32.916..1914.618 rows=3729876 loops=1)
         Filter: (((moderation)::text <> ALL ('{junk,spam}'::text[])) AND (social_creation_time >= 1391716800) AND (social_creation_time <= 1392839999) AND (project_id = 7))
         Rows Removed by Filter: 501865
Total runtime: 2566.071 ms

PDATE#2整数の列を作成し、20140220(YYYMMDD)のような日付を保存すると思います。 stackexchange、あなたはどう思いますか、それはパフォーマンスの向上ですか?

PS:私の悪い英語をごめんなさい

3
     Sort Method: external merge  Disk: 92048kB

もっと投げるwork_mem問題に。さらに多く。試してください:

SET LOCAL work_mem = '300MB';

多数の同時接続で実行している場合は、システムRAMを使い果たす可能性があることに注意してください。したがって、個々のセッションでのみSETになります。

集計の行数の見積もりは少し危険ですが( http://explain.depesz.com/s/RXbq )、それほど悪くはありません。キラーはそのような大きな種類のようです。

5
Craig Ringer

@Craigと@dezsoによる良いアドバイスに加えて:

統計学

カウントは940689です。

しかし、クエリプランは次のように述べています。

_Seq Scan on posts  (cost=0.00..205984.62 rows=**3387494** width=12)
_

そして、あなたのカウントは選択に基づいています:

_Rows Removed by Filter: 404218
_

4344907(3940689 + 404218)>>3387494。あなたの統計は最新ではありませんautovacuum settings に問題がある可能性があります。これはとりわけANALYZEを自動的に実行します。全体的なDBパフォーマンスにとって非常に悪い。何かを再試行する前に実行中のクエリの場合:

_ANALYZE posts
_

しばらくの間テーブルをロックする余裕がある場合は、実行します

_VACUUM FULL ANALYZE posts
_

家を掃除する。 詳細はこちら

インデックス

数値は、クエリがすべての行の約90%を使用していることを示しています。したがって、順次スキャンは、 カバーするインデックス(インデックスのみのスキャン) を除いて、可能なインデックススキャンよりも高速になります。 Postgres 9.2 +が必要です。 トピックに関するPostgres Wiki を必ずお読みください。

列の長いリストから2つの小さな列のみを使用するため、そのようなインデックスは小さくて高速になります。その間、そして全体的な要件に応じて、次のような調整されたインデックスは最大のパフォーマンスを絞り出します:a partialfunctionalmulticolumncoveringindex -書き込み操作には多少のコストがかかります:

_CREATE INDEX test_idx ON posts (
   date_trunc('hour', timestamp 'Epoch' + social_creation_time * interval  '1 sec')
  ,network)
WHERE moderation NOT IN ('junk','spam')
AND   project_id = 7                         -- ??
AND   social_creation_time BETWEEN 1391716800 AND 1392839999 -- ??
_

実際のWHERE条件は実際の要件によって異なり、このインデックスを使用するクエリにほぼ同じ形式で追加する必要があります。インデックスから使用されていない行を削除します。数行以上を除外する条件のみを使用し、クエリに必要な行のスーパーセットを調整します。

一般に、カバリングインデックスは、かなり静的なテーブルに適しています。 Wikiを読んでください。簡単なテスト:

_SELECT relallvisible, relpages
FROM   pg_class
WHERE  oid = 'posts'::regclass
_

relallvisiblerelpagesよりもはるかに小さい場合、可能性は高いです。これを試す前に、autovacuumが適切に動作していることを確認してください。

また、機能的な側面なしでテストして、どちらが使用されているか/より高速かを確認します。

_CREATE INDEX test_idx ON posts (social_creation_time, network)
WHERE moderation NOT IN ('junk','spam')
AND   project_id = 7                         -- ??
AND   social_creation_time BETWEEN 1391716800 AND 1392839999; -- ??
_

部分的なカバーインデックスを扱う関連する回答の検索

テーブルレイアウト

最後に、テーブル定義でintegertextの列が入れ替わり、データの配置とパディングが原因でテーブルがかなり肥大化しています。この関連回答の詳細:
読み取りパフォーマンスのためのPostgreSQLの構成

これらの行に沿ってテーブルを再作成します。

_CREATE TABLE post (
  post_id serial PRIMARY KEY,
  project_id integer,
  created_at timestamp,
  updated_at timestamp,
  keyword_id integer,
  author_id integer,
  source_id integer,
  subject_id integer,
  conversation_id integer,
  social_creation_time integer,

  server_id  text,   -- could be integer?
  social_id  text,
  moderation text,
  network    text,
  url        text,
  location   text,
  mood       text
);
_

少し小さくなり、全体的なパフォーマンスが向上します。
varchar(255)の代わりにtextを使用する理由

11