web-dev-qa-db-ja.com

MyISAMはInnoDBの約5倍遅い

MyISAMは単純なテーブル用であり、InnoDBよりもアーキテクチャが速いため、選択すると思いました。したがって、このテーブルのエンジンをInnoDBからMyISAMに変更しました。

CREATE TABLE `table1` (
  `DateTime` datetime NOT NULL,
  `BidHigh` decimal(11,5) NOT NULL,
  `BidLow` decimal(11,5) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin COMMENT='1 minute rates';

ALTER TABLE `table1` ADD PRIMARY KEY (`DateTime`);

これらの条件が適用されます:

  1. 私はそれをSLES 15.1でテストしていますVM with 5 GB RAMおよび私のCPUで8つのCPUコアがあり、他のものにサービスを提供する必要はありませんVM [他にないという情報だけVM結果に影響を与える可能性がある]].
  2. PHPスクリプトは、以下の何千ものSELECTステートメントを実行しています。
  3. データベースには上記のテーブルが24個あり、PHPスクリプトでアクセスされます。
  4. 各テーブルには〜800k行があります。
  5. 各テストの間にサーバーを再起動して、常に同じ条件が存在することを確認します。違いが非常に大きいという事実のため、平均を得るために複数のテストを実行していません...

実行時間:

  • InnoDBエンジンを使用すると、スクリプトは199秒実行されます。
  • MyISAMエンジンを使用すると、スクリプトは1'026秒実行されます。 5倍以上長い。

これらのSELECTステートメントを実行しています:

SELECT `DateTime` FROM table1
     WHERE `DateTime` BETWEEN '2018-12-27 07:50:00' AND '2199-12-31 23:59:00'
        AND BidHigh > 0.96604
     ORDER BY `DateTime` LIMIT 1;

-および-

SELECT MIN( BidLow ) FROM table1
    WHERE `DateTime` BETWEEN '2018-12-27 07:45:00' AND '2199-12-31 23:59:00';

OK、インデックスの問題だとわかりました。これら2つのインデックスを追加する

ALTER TABLE `table1` ADD UNIQUE `BidHigh` (`DateTime`, `BidHigh`);

-および-

ALTER TABLE `table1` ADD UNIQUE `BidLow` (`DateTime`, `BidLow`);

パフォーマンスの問題を修正し、スクリプトは245秒を必要としますが、それでもInnoDBよりも遅くなります-これは本当に私が期待したものではありません...

これらのインデックスをInnoDBバージョンに追加しても、パフォーマンスは向上しません。


私の質問:

  1. なぜInnoDBはこれらのインデックスを必要とせず、さらに高速なのですか?
  2. より良い解決策はありますか?
  3. そして、MyISAMへの変更がこのような恐ろしいパフォーマンスを引き起こしたと私がどう考えているかを完全に誤解していました。
1
Peter VARGA

1)InnoDBもインデックスを使用すると高速になります。

2)適切なインデックス付けを備えたInnoDBが最良のソリューションです。

3)MyISAMは、10年以上もの間、ほとんどのワークロードでInnoDBよりも低速でした。 2つの間でメモリとキャッシングの動作に基本的な違いがあります。

この場合、InnoDBは主キーによって最初の行を選択していました。 InnoDBテーブルは主キーによってクラスター化されているため、これは非常に高速であり、PKはテーブルを作成したときからすでにメモリ内にある可能性があります。

BidHighでインデックスを作成すると、さらに高速になります。

MyISAMを使用する圧倒的な理由がない限り、使用しないでください。そして、あなたが圧倒的に良い理由があると思うなら、それらは2020年にはほとんどないのでそれを再調査する必要があります。

2
Gordan Bobic

「MyISAMの方がいい...」は、非常に古くなっている古い「妻の物語」です。 InnoDBを使用します。

2つのエンジンはまったく異なる方法でインデックスを使用します。

PRIMARY KEY(DateTime-同じ秒の2つのレコードを保存しないでください。 PKは固有です。

クエリ1

_SELECT `DateTime` FROM table1
     WHERE `DateTime` BETWEEN '2018-12-27 07:50:00' AND '2199-12-31 23:59:00'
        AND BidHigh > 0.96604
     ORDER BY `DateTime` LIMIT 1;
_

これには2つの範囲が含まれるため、MyISAMまたはInnoDBのどちらにも適切なインデックスを構築することは基本的に不可能です。オプティマイザーはDateTimeで始まるインデックスを使用し、他の列のすべての行をテストします。可能なインデックスを調べてみましょう:

_PRIMARY KEY(DateTime)
_

MyISAMの場合、DateTimeに基づくBTreeと、データ行へのポインターがあります。データ行を調べてBidHighを取得し、その値を確認します。

InnoDBの場合、データは日時順に並べられます。したがって、BidHighを取得するための追加はありません。勝者:InnoDB。

どちらのエンジンでも、オプティマイザーmightは、ソートを回避してLIMITに到達するのに十分スマートです。ただし、テストする必要がある行数に依存するため、これは危険です。このデータの変動により、選択したクエリプランにより、5倍(または500倍)のスローダウンが容易に発生する可能性があります。 INDEX(DateTime、BidHigh)

これはMyISAMを「カバーする」インデックスにすることで、MyISAMの非効率性を解決します。 InnoDBにとって、それはほとんど無駄です。 PKは基本的にINDEX(DateTime, BidHigh, BidLow)であり、2列のインデックスよりもわずかに悪いだけです。

_INDEX(BidHigh, DateTime)
_

これはおそらく高速ですifBidHignの範囲テストに一致する行がlot少ない場合、DateTimeの範囲テストよりも少なくなります。しかし、LIMITに到達する前に並べ替えが存在します。

_EXPLAIN SELECT ..._を使用して、何が行われたかを確認します。

たぶん空間

最初のクエリには2Dインデックスが必要ですが、これはINDEXが提供するものではありません。 「緯度/経度」という用語で表現された5つのオプションについて説明します。 http://mysql.rjweb.org/doc.php/find_nearest_in_mysql

SPATIALの使用は、最初のクエリでは実行可能かもしれませんが、2番目のクエリでは実行できない可能性があります。

クエリ2

_SELECT MIN( BidLow ) FROM table1
    WHERE `DateTime` BETWEEN '2018-12-27 07:45:00' AND '2199-12-31 23:59:00';
_

InnoDBの場合:PRIMARY KEY(DateTime)は、約1年分のデータのスキャンにつながります。

MyISAMの場合、正確にINDEX(DateTime, BidLow)でない限り、インデックスを使用することはできません。

23:59:00

その日の最後の1分間に入札を想定していませんか?

使用を検討してください

_WHERE DateTime >= '...
  AND DateTime  < '...-01-01'
_
2
Rick James

コメントフィールドの長さが十分でないため、コメントを回答として追加しています。

私は Gordan Bobic の回答を受け入れました。彼は新しいメンバーなので、彼の回答を受け入れることで得られる評判はpushingだと思います。

彼の発言の一部が Rick James の回答で確認されたので、それは私に示しています Gordan Bobic は何が起こっているのかを理解しています。

ゴーダンの発言:

MyISAMを使用する圧倒的に正当な理由がない限り、使用しないでください。そして、もしあなたが圧倒的に良い理由があると思うなら、それらは2020年にはほとんどないのでそれを再調査する必要があります。

自分の理解が間違っていることに気づき、使用したエンジンに関して自分でresetしなければならなかったので、私にとって非常に重要なトリガーでした。

1つの質問に当てはまらないため、問題全体については触れませんでした。両方の回答を読んだ後、データベースを再構築する必要があることに気付きました。したがって、たとえば、いくつかのJSONテーブルにInnoDBフィールド[最大長は〜6kバイト、平均長は〜2kバイト]がありました。

行数が多いため、これらのInnoDBテーブルのサイズも大きく、毎日増加していました。これが、これらのInnoDBテーブルをMyISAMに変換し始めた理由です。しかし、前述のとおり、これによりパフォーマンスが大幅に低下したため、この質問をしました。

これらのJSONfieldsを、2つのフィールド(主キーとMyISAMフィールド]のみを持つ非常に単純なJSONテーブルに抽出しました。これによりサイズが小さくなり[〜30%]、パフォーマンスに影響を与えません。

多分私は今少し混乱しているように聞こえるかもしれませんが、全体的な文脈でそれは私を大いに助け、問題を解決しました!

1
Peter VARGA