3億以上のレコードを持つMySQLテーブルの最適化手法にはどのようなものがありますか?

StackOverflow https://stackoverflow.com/questions/444099

質問

JVMからのJMXデータを多くのサーバーに約90日間保存することを検討しています。このデータは、ヒープサイズやスレッド数などの統計になります。これは、テーブルの1つに約3億8800万のレコードがあることを意味します。

このデータから、Mbeanから取得した統計を比較できるように、いくつかのグラフを作成しています。これは、タイムスタンプを使用して一定の間隔でいくつかのデータを取得することを意味します。

それで、本当の質問は、とにかくテーブルまたはクエリを最適化して、これらのクエリを妥当な時間で実行できるようにすることですか?

ありがとう、

ジョシュ

役に立ちましたか?

解決

できることはいくつかあります:

  1. 実行中のクエリに一致するようにインデックスを構築します。 EXPLAIN を実行して、実行されたクエリの種類を確認します。可能な限りインデックスを使用するようにしてください。

  2. テーブルを分割します。パーティショニングは、特定の(集約)キーによって大きなテーブルをいくつかの小さなテーブルに分割するための手法です。 MySQLはこれを verから内部的にサポートしています。 5.1

  3. 必要に応じて、クエリのコストの高い部分をキャッシュするサマリーテーブルを作成します。次に、サマリーテーブルに対してクエリを実行します。同様に、一時メモリ内テーブルを使用して、テーブルの簡略化されたビューを前処理段階として保存できます。

他のヒント

3つの提案:

  1. インデックス
  2. インデックス
  3. インデックス

p.s。タイムスタンプについては、パフォーマンスの問題が発生する可能性があります。MySQLがDATETIMEおよびTIMESTAMPを内部で処理する方法によっては、タイムスタンプを整数として保存する方が適切な場合があります。 (1970年以降の秒数など)

さて、まずは、「オフライン」を使用することをお勧めします。オンデマンドで生データを照会するのではなく、「グラフ対応」データを生成する処理(ほとんどの一般的な場合)。

MYSQL 5.1を使用している場合、新しい機能を使用できます。 ただし、多くのバグが含まれていることに注意してください。

最初にインデックスを使用する必要があります。 これで十分でない場合は、パーティションを使用してテーブルを分割してみてください。

これも機能しない場合は、負荷分散を試すこともできます。

いくつかの提案。

おそらくこのようなものに対して集計クエリを実行するので、データをテーブルにロードした後(またはその間に)、データを事前に集計する必要があります。たとえば、時間ごとまたはユーザーごとに合計を事前に計算する必要があります、または週ごとに、アイデアを得て、レポートグラフに使用するキャッシュテーブルに保存します。データセットを1桁縮小できるなら、それでいいです!

  

これは、タイムスタンプを使用して一定の間隔でいくつかのデータを取得することを意味します。

つまり、これは過去X日間のデータのみを使用するということですか?

テーブルから古いデータを削除するのは、数千万行を削除する必要がある場合、恐ろしく遅くなる可能性があります。パーティション化はそのために最適です(古いパーティションを削除するだけです)。また、同じ期間のすべてのレコードをディスク上でまとめてグループ化するため、キャッシュの効率が大幅に向上します。

MySQLを使用する場合、MyISAMテーブルを使用することを強くお勧めします。クラッシュ防止機能やトランザクションが得られず、ロックが愚かではありませんが、テーブルのサイズはInnoDBよりもはるかに小さいため、RAMに収まるため、アクセスがはるかに速くなります。

大きなアグリゲートには大量のシーケンシャルディスクIOが含まれることがあるため、RAID10(またはSSD)などの高速IOシステムはプラスです。

  

とにかくテーブルまたはクエリを最適化してこれらのクエリを実行できるようにする   妥当な時間内に?

それはテーブルとクエリに依存します。詳細を知らずにアドバイスをすることはできません。

大きな集約と結合を使用した複雑なレポートクエリが必要な場合、MySQLは派手なJOIN、ハッシュ集約、または実際に有用な他のものをサポートしていないことに注意してください。キャッシュされたテーブルでは有効であり、ランダムアクセスが関与する場合は他のケースでは絶対に残酷です。

Postgresでテストすることをお勧めします。大きな集合体の場合、よりスマートなオプティマイザーはうまく機能します。

例:

CREATE TABLE t (id INTEGER PRIMARY KEY AUTO_INCREMENT, category INT NOT NULL, counter INT NOT NULL) ENGINE=MyISAM;
INSERT INTO t (category, counter) SELECT n%10, n&255 FROM serie;

(serieにはn = 1 .. 16000000の16M行が含まれています)

MySQL    Postgres     
58 s     100s       INSERT
75s      51s        CREATE INDEX on (category,id) (useless)
9.3s     5s         SELECT category, sum(counter) FROM t GROUP BY category;
1.7s     0.5s       SELECT category, sum(counter) FROM t WHERE id>15000000 GROUP BY category;

このようなpgのような単純なクエリでは、約2〜3倍高速です(複雑な結合が関係している場合、差はさらに大きくなります)。

  1. SELECTクエリの説明
  2. LIMIT 1一意の行を取得する場合 SELECT * FROMユーザーWHERE state = 'Alabama' //間違っています ユーザー1からの選択WHERE状態= 'アラバマ'制限1

  3. 検索フィールドのインデックス作成 インデックスは、主キーまたは一意キーだけのものではありません。テーブルに検索する列がある場合、ほとんどの場合、それらの列にインデックスを付ける必要があります。

  4. 結合に同じ列タイプをインデックス付けして使用する アプリケーションに多数のJOINクエリが含まれている場合、結合する列が両方のテーブルでインデックス付けされていることを確認する必要があります。これは、MySQLが内部的に結合操作を最適化する方法に影響します。

  5. RAND()で並べ替えない 結果からランダムな行が本当に必要な場合は、もっと良い方法があります。追加のコードが必要ですが、データが大きくなるにつれて指数関数的に悪化するボトルネックを防ぐことができます。問題は、MySQLはソートして1行だけを表示する前に、テーブル内のすべての行に対してRAND()操作(処理能力が必要)を実行する必要があることです。

  6. VARCHARではなくENUMを使用 ENUM型の列は非常に高速でコンパクトです。内部的にはTINYINTのように保存されますが、文字列値を含めて表示できます。

  7. 可能な場合はNOT NULLを使用 NULL値を使用する特別な理由がない限り、常に列をNOT NULLに設定する必要があります。

    " NULL列では、値がNULLかどうかを記録するために行に追加のスペースが必要です。 MyISAMテーブルの場合、各NULLカラムは1ビット余分に必要となり、最も近いバイトに切り上げられます。"

  8. IPアドレスをUNSIGNED INTとして保存する クエリでは、INET_ATON()を使用してIPを整数に変換し、INET_NTOA()を使用して整数に変換できます。 PHPにはip2long()およびlong2ip()と呼ばれる同様の関数もあります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top