web-dev-qa-db-ja.com

この特定のビューに対するクエリを高速化できる方法はありますか?

ビューといくつかの結合を介してクエリを実行しているPostgreSQLデータベースに3つのテーブルがあります。

CREATE TABLE network_info (
  network         CIDR          NOT NULL,
  some_info       TEXT          NULL,
  PRIMARY KEY (network)
);

CREATE TABLE ipaddr_info (
  ipaddr          INET          NOT NULL,
  some_info       INT           NULL,
  PRIMARY KEY (ipaddr, some_info)
);

CREATE TABLE ipaddrs (
  addr            INET          NOT NULL,
  PRIMARY KEY (addr)
);

CREATE VIEW ipaddr_summary AS
SELECT DISTINCT
  i.addr                  AS ip_address,
  a.some_info             AS network_info,
  COUNT(b.ipaddr)         AS ip_info_count
FROM ipaddrs AS i
LEFT JOIN network_info AS a
  ON (i.addr << a.network)
LEFT JOIN ipaddr_info AS b
  ON (i.addr = b.ipaddr)
GROUP BY i.addr, a.some_info
;

現在、すべてのテーブルの行数は約15万行で、実行に非常に長い時間(約3時間)が必要ですSELECT * from ipaddr_summary; PostgreSQL 9.3を実行する2Gのメモリを搭載したIntel Pentium 4 2.8GHzデュアルコア。

この特定のスキーマまたはビューを再構成または最適化してクエリを高速化する方法はありますか?それともハードウェアの問題ですか?クラウドで強力なVM=を起動してテストしますが、ハードウェアを増やすだけで最適化する方法があるかどうかを確認したいと考えていました。

3
Bryan

ハードウェアの問題もある可能性があります-どのように知る必要がありますか?しかし、クエリには確かに問題があります。

最初にまず、DISTINCTVIEW定義から削除します。それは何もしていません(しかし、物事を複雑にし、遅くしています)。 SOに関する説明付きの関連回答:

この(クリーンアップされた)クエリに到達します。

SELECT i.addr      AS ip_address
     , a.some_info AS network_info
     , COUNT(b.ipaddr) AS ip_info_count
FROM   ipaddrs           i
LEFT   JOIN ipaddr_info  b  ON i.addr = b.ipaddr
LEFT   JOIN network_info a  ON i.addr << a.network
GROUP  BY 1,2;

次に、クエリは非常にうまくいかないように疑わしく見えます。 2つの無相関結合は行を乗算できます。

各テーブルに15万行あるため、huge(意図しない)デカルト積の可能性があります。私の教育的な推測、あなたは本当にこれが欲しい:

SELECT addr        AS ip_address
     , a.some_info AS network_info
     , b.ip_info_count
FROM   ipaddrs i
LEFT   JOIN (
   SELECT  ipaddr AS addr, count(*) AS ip_info_count
   FROM    ipaddr_info
   GROUP   BY 1
  ) b USING (addr)
LEFT   JOIN network_info a ON i.addr << a.network

今はGROUP BYも外側のSELECTには不要だと思います。カウントを修正するだけでなく、これは数桁も速くなり、プロキシのクロス結合が回避されます。

最初にipaddrsに結合し(特に、述語から行をフィルタリングしている場合)、次に集計するか、表示されているようにサブクエリで最初に集計します。このバリアントの有用性は、データの分散に大きく依存します。 fewipaddrの数が多い場合に最適です。詳細:

最後にindex supportが必要です。 ipaddraddrの等価性は、PRIMARY KEYのデフォルトのbtreeインデックスでカバーされます。とにかく、テーブル全体に対するクエリは、おそらく順次スキャンを使用しています。

「に含まれる」演算子<<演算子の場合、GINまたはGistインデックスが必要です。最良のオプションは、Postgresの新しい inet_opsGist index operator class です9.4(データ型inetおよびcidrをサポート):

CREATE INDEX ON network_info USING Gist (network inet_ops);

インデックスを単純なINNER(またはOUTER)結合で使用できるかどうかは不明です。現在テストできません。 たぶんインデックスを利用するには、相関サブクエリまたはLATERAL結合が必要です。

SELECT addr AS ip_address
     , a.network_info
     , b.ip_info_count
FROM   ipaddrs i
LEFT   JOIN (
   SELECT  ipaddr AS addr, count(*) AS ip_info_count
   FROM    ipaddr_info
   GROUP   BY 1
  ) b USING (addr)
LEFT   JOIN LATERAL (
   SELECT some_info AS network_info
   FROM   network_info
   WHERE  network >> i.addr
   ) a ON TRUE;

古いバージョンでのインデックス作成に関するアドバイス:

9

あなたの<<はインデックスを利用していないと思います。あなたの主キーがbtreeインデックスを作成し、その特定の操作をインデックス可能にするために必要なのはGistインデックスだからです。 (説明を使用して、分析を確認します)。そのインデックスが使用されていない場合は、

のようなことをやってみてください

   CREATE INDEX idx_network_info_network_Gist
     ON network_info USING Gist(network inet_ops); 

 CREATE INDEX idx_ipaddr_info_ipaddr_Gist
    ON ipaddr_info USING Gist(ipaddr inet_ops);

そして、あなたのaddrとipaddrカラムで同様に行います。

2
LR1234567