web-dev-qa-db-ja.com

MySQL InnoDBよりもPostgresの方が高速なクエリ

同じスキーマ/クエリに対するMySQLとPostgreSQLのパフォーマンスの違い を読みました。記事の簡単な説明を次に示します。

PostgreSQLテーブルはヒープテーブルです(はクラスター化インデックスがないことを意味します)...(Postgres)テーブルの主キー検索にはヒットが必要ですインデックス、ファイル内の場所を検索し、ヒープテーブルをヒットして、レコードをプルします。これは、ランダムなディスクI/Oの数を意味します... InnoDBは異なるアプローチを使用します。 InnoDBでは、テーブルはBツリーインデックスです(クラスター化され、物理的にソートされます)... PKルックアップに必要なランダムディスクI/Oは少なくなります...同時に、インデックススキャンでは、1つではなく2つのインデックスをトラバースする必要があります(index->​​ PK index->​​ table row)。つまり、主キー以外のインデックスの使用は遅くなり、順次スキャンはさらに遅くなります。

どの種類のクエリがMySQL InnoDBよりもPostgresの方が高速ですか?

MySQLでPKルックアップがはるかに優れている理由を理解しています。わかりません:

  1. 2つのインデックス(InnoDB、非PKインデックスのルックアップ)の検索がはるかに遅いのはなぜですか? 2倍以上のI/OまたはCPUが必要ですか? PKルックアップブーストの大きなメリットを補うことができますか?
  2. InnoDBの順次スキャンが遅いのはなぜですか?

追伸インターネットは、Postgresが複雑なクエリとサブクエリに適していると述べていますが、なぜそれが良いのかわかりません。

6
VB_

炎上戦争を回避するために、実際にはベンチマークではなく、各ストレージがクエリを処理する方法を一目見ます。この表を参照として使用します(両方のRDBMSで実行するようにコードを少し変更する必要があります):

CREATE TABLE employees (
    emp_id int,
    name varchar,
    depto_no int,
    salary decimal,
    CONSTRAINT emp_pk PRIMARY KEY (emp_id);
);
CREATE INDEX emp_depto_idx ON employees (depto_no);

PostgreSQLには3つの構造があります。

  1. employeesヒープ。これは基本的に、(テーブルを想像するのと同じように)順次格納されるテーブルです。
  2. emp_pkインデックス(これも主キーです)。Bツリーインデックスとして格納され、各要素にはemployeeのヒープへのポインターがあり、ディスク内の正確なページ/オフセットが含まれます。
  3. emp_depto_idxインデックス。これは、一意性を強制しないことを除いて、ヒープへのポインタを持つBツリーであるemp_pkに似ています。

MySQL InnoDBでは、2つしかありません。

  1. emp_pkemployeesは、1つの構造体、emp_id列で順序付けされたBツリーとして格納され、他の列の値をリーフノードのペイロードとして保持します。
  2. emp_depto_idxインデックスはBツリーであり、各要素にはその行を参照するemp_id値があります(物理ロケーションポインターではありません)。

主キーの検索

mySQLのPKルックアップがはるかに優れている理由

私はあなたがそれを知っていることを知っていますが、それを明確にしましょう。

次のようにクエリすると:

SELECT * FROM employees WHERE emp_id = 10;

PostgreSQLでは、emp_pkインデックス(Bツリーインデックスで1スキャン)をナビゲートし、ページ/オフセットを取得して、employeesヒープテーブル(1directページ/行フェッチ、実際にはスキャンではありません)。したがって、インデックスで1つのスキャン、ヒープで1つの直接フェッチとなります。

MySQLでは、すべての情報がすでにそこにあるため、主キーインデックス(Bツリーインデックスの1回のスキャン)をナビゲートするだけで、他のルックアップは必要ありません。したがって、単にインデックスを1回スキャンするです。

したがって、PostgreSQLは1回のスキャンと1回のフェッチを実行する必要がありますが、MySQLは1回のスキャンを実行するだけです。

セカンダリインデックスルックアップ

2つのインデックスの検索(InnoDB、非PKインデックスの検索)がはるかに遅いのはなぜですか? 2倍以上のI/OまたはCPUが必要ですか? PKルックアップブーストの大きなメリットを補うことができますか?

次に、この別のクエリを想定します。

SELECT * FROM employees WHERE depto_no = 14;

PostgreSQLでは、他のものとそれほど変わりません。 emp_depto_idxをスキャンし、返された各行について、ヒープから直接値をフェッチします。したがって、インデックスでの1回のスキャン、および一致した各行のヒープでの直接フェッチです。

MySQLではemp_depto_idx(インデックスの1回のスキャン)をスキャンし、返された各行に対して参照emp_idを取得して、主キーインデックスをスキャンします。したがって、一致した各行について、セカンダリインデックスで1回のスキャン、およびプライマリインデックスで1回のスキャンとなります。

違いを見ます? PostgreSQLはスキャンを実行してから、一致する各行を直接ポインタでフェッチしますが、InnoDBは最初に同様のスキャンを実行し、次に一致する各行に対して別のスキャンを実行します。さて、部門14の従業員が少ない場合は十分高速かもしれませんが、従業員が増えると本当に遅くなります(もちろん、両方のRDBMSで遅くなりますが、曲線はおそらくInnoDBの方が高い)。

フルスキャン

InnoDBの順次スキャンが遅いのはなぜですか?

単純な答えです。これは実際には「順次」ではないためです...

さて、最も単純な(そして確かに遅い)クエリを見てみましょう:

SELECT * FROM employees;

PostgreSQLでは、employeesヒープ全体を1行ずつ物理的な順番で簡単にスキャンできます(ここでの挿入順序に関係なく、タプルとページが物理的にどのように配置されているかが重要です)。

InnoDBでは、インデックスを走査する必要があります。これは、ランダムスキャンが増えることを意味します(インデックスページは必ずしも物理的および論理的に同じ方法で並べられるとは限らないため)。

磁気ディスクについて考えると、違いは明白です。シーケンシャルアクセスはランダムアクセスよりもはるかに高速であることがわかります。 SSDの場合、先読みなどの順次アクセスには依然として利点がありますが、必ずしもそうとは限りません。したがって、ほとんどのシナリオでは、PostgreSQLのフルスキャンはInnoDBのスキャンよりも高速になります。少なくともかなり大きなテーブルの場合(「大きな」とは定義していないため、実際に試して、違いが本当に重要な場所を確認する必要があります。多くの場合重要です)。両方のRDBMSにとって最良の方法は、可能であれば、モデルとクエリを設計して完全スキャンを回避することです。

複雑なクエリ

インターネットは、Postgresが複雑なクエリとサブクエリに適していると言っていますが、なぜそれが良いのかわかりません。

これは非常に大きなトピックであり、おそらくより多くの炎上戦争を生成するトピックなので、いくつか例を挙げましょう。 PostgreSQLは複雑なクエリに適しているとよく言われますが、MySQLにはない(まだパフォーマンスを考慮していない)クエリ機能を単純に考えれば、次のようになります。

  • 共通テーブル式(CTE)
  • ウィンドウ関数
  • LATERALが参加
  • 配列
  • JSONの型、関数、演算子
  • 等.

それ以外にも、これら2つのプランナーとエグゼキューターには多くの違いがあります。たとえば、PostgreSQLはネストされたループ、ハッシュ結合、マージ結合を使用して結合を実行できますが、MySQLはネストされたループを使用してのみ実行できます。それにもかかわらず、MySQLのネストループアルゴリズムには多くの最適化があり、PostgreSQLのプランナーではより難しい選択が行われています。

最後の言葉

この回答はトピックを一目見ただけであり、インデックスのみのスキャン、バキュームとアンドゥ、並列処理など、パフォーマンスに関しては2つのRDBMSについて考慮すべきことがまだたくさんあります。本当のことは、簡単に言うと、一方が他方よりも高速であるとすると、ある環境では高速になり、他の環境では高速になることは明らかです(それはあなたにとってですか)。

17
MatheusOl