1

私の目標は、高速読み取りのために MySQL テーブルに約 6000 万行を保存することであり、適切に挿入も続行します。

製品設計では、これらの 6000 万行は自然に 3000 個のチャンクに分割できるため、1 つの 60M テーブルを 3000 個のテーブルに分割するテーブル シャーディング戦略を作成することにしました。

次のテストのために 300 万のデータを取得しました。

  1. 1 つのテーブルに 300 万行: この 300 万のデータの平均挿入時間は 80 秒で、1000 クエリ (各クエリはこの 300 万のデータ テーブルから 1000 行をフェッチする) ごとに約 10 秒かかります。

  2. 平均 300 万行を 3000 テーブルに分割: 300 万データを 3000 テーブルに挿入: 79 秒 (それほど速くはありません)。3000 個のテーブル (各テーブルには 1000 行あります) に対する 1000 回のクエリの平均: 120 秒 (上記よりも 12 倍遅い)

何故ですか?テーブルは 3000 個ありますが、基本的には MySQL で管理されているファイルであり、各クエリは 1000 行しかない 1 つのテーブルしかヒットしません。

次の構成の 15G RAM を搭載した 8 コア マシンで実行しています。

open_files_limit 300000
table_open_cache 100000

シミュレーションを 2 ~ 3 回再試行した後、次のように MySQL の「openED ファイル」も検索しました。

Opened_tables: 9463

どうすればこの問題から抜け出すことができますか?

----------- 編集とその他の考え -----------

現時点では、テーブルのシャーディングの可能性を試しているだけです。MySQL Merge エンジンがこの方向に少し役立つかもしれません。

一方で、パーティションも悪くないアイデアかもしれません... たとえば、MySQL の範囲によるパーティションでは、範囲を 1,000 万にすることができ、60M のテーブルは 6 つのパーティションを持つテーブルになります...クエリと挿入の両方が高速になりますか?

----------- 試行中のテーブル パーティションの更新 -----------

以下にもコメントされているように、テーブル シャーディングの代わりに、特に同じテーブル名を維持し、既存のコードへの影響が最小限である場合は、テーブル パーティションも良い解決策になるのではないかと考えていました。

この 6000 万のテーブルに 6 つのパーティションを作成しようとしました。

1) 最初に、次の疑似コードのようなものを作成しました。

CREATE TABLE `datatable` (  
`id` int(11) NOT NULL AUTO_INCREMENT,  
`type` int(11) NOT NULL DEFAULT 0,  
`description` varchar(255),  
`datimeutc` datetime,  
`datimelocal` datetime,  
`value` double,  
PRIMARY KEY (`id`), 
KEY INDEX_TYPE ON (type)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1  
PARTITION BY RANGE (id) (  
    PARTITION p0 VALUES LESS THAN (10000000),  
    PARTITION p1 VALUES LESS THAN (20000000),  
    PARTITION p2 VALUES LESS THAN (30000000),  
    PARTITION p3 VALUES LESS THAN (40000000),  
    PARTITION p4 VALUES LESS THAN (50000000)  
    PARTITION p5 VALUES LESS THAN MAXVALUE
);

そして、結果はかなり良いです。テスト用に 300 万のデータをインポートするのに約 1 分かかり、6000 万のデータすべてをインポートするには合計 63 分かかります。

各クエリの検索時間 (60M のパーティション ベースのテーブルから 20000 行をフェッチする) は約 90 ミリ秒です。単一の 6000 万テーブルに対するクエリ パフォーマンスの比較データはありませんが、90 ミリ秒は妥当な値ですか?

2) MySQL にはパーティションの一意のキーに制限があるため、受信する個々のクエリを単一のパーティションに制限することを期待して、フィールド「タイプ」でパーティションを試しました。擬似コードは次のようになります。

CREATE TABLE `datatable` (  
`id` int(11) NOT NULL AUTO_INCREMENT,  
`type` int(11) NOT NULL DEFAULT 0,  
`description` varchar(255),  
`datimeutc` datetime,  
`datimelocal` datetime,  
`value` double,   
KEY (`id`), 
KEY INDEX_TYPE ON (type)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1  
PARTITION BY RANGE (type) (  
    PARTITION p0 VALUES LESS THAN (500),  
    PARTITION p1 VALUES LESS THAN (1000),  
    PARTITION p2 VALUES LESS THAN (1500),  
    PARTITION p3 VALUES LESS THAN (2000),  
    PARTITION p4 VALUES LESS THAN (2500)  
    PARTITION p5 VALUES LESS THAN MAXVALUE
);

このとき、60M のデータを挿入すると、最初のケースに比べて挿入時間が非常に長くなります。まだ結果は出ていませんが、今のところ4Mのデータを挿入するだけで既に3時間かかっています...

何故ですか?

私が考えているのは、おそらく60Mを順番に挿入することです。つまり、行IDは1から60000000までです。したがって、ケース1では、基本的に挿入する最初のパーティションを開いてロックし、最初の10Mが挿入されたら、パーティション2を開いて挿入します継続する。

一方、パーティションの 2) の場合、(「id」ではなく「type」で設計されている) 6 つのパーティションすべてを頻繁かつランダムに開く必要があるため、テーブルのロックとロック解除に時間がかかりすぎましたか? それが理由でしょうか?

4

2 に答える 2

1

三千の破片?それはあまりにも多すぎます。mysqld サーバーは、複数のシャードのデータ ファイルにアクセスするためにスクランブルをかける必要があるため、速度が低下しています。

1 つのテーブルで 6000 万行というのは大きな数字ですが、あなたが説明したサーバー ハードウェアでは多すぎません。

このようなアプリケーションでパーティショニングを行う最も重要な理由は、多数の古くなった行をすばやく簡単に削除できるようにすることです。行に日付が付けられている場合は、たとえば月ごとに分割できます。

このテーブルを分割する必要がある場合は、4 つのパーティションで作業してみてください。ただし、パフォーマンスの必要性によって強制されない限り、シャードしないでください。もし私があなたなら、アプリケーションの残りの部分を適切に動作させるでしょう。次に、すべてが機能するようになったら、システムのすべてのパフォーマンスの問題 (ボトルネック) を評価し、重大度の順に対処します。

私の勘では、この大きなテーブルが深刻なパフォーマンスの問題を引き起こしている可能性は低いと思います。

于 2013-07-04T17:38:18.760 に答える
1

はい、MySQL でテーブルを分割することは、次のシナリオの一般的な良い方法です。

  1. テーブルが大きくなりすぎて、通常のテーブルOP時間が耐えられなくなった(パフォーマンスが大幅に低下した)
  2. テーブル内のホット データの割合が比較的小さい
  3. データには時間枠があります (データはタイムリーにアーカイブまたはパージできます)
  4. 並行性を高めるために、このシナリオでは、データは通常、さまざまな分離された物理サーバーまたは異なるストレージ システムに分散されます。

あなたの元の投稿では、最初のシナリオに主に関心を持っていたと思いますので、それについてもっと議論しましょう.

テーブルが非常に大きい場合、パフォーマンスが劇的に低下するのはなぜですか? そして、サイズの境界は何ですか?それはすべてメモリに関するものです。FusionIO やその他の SSD システムを購入していない限り、I/O がディスクにヒットするときは常に急激な曲線になります。通常、SATA/SAS ディスク アレイは約 50 ~ 200 のランダム IOPS (BBU で保護された書き込みキャッシュを使用) しか実行できません。これは、DDR の 200,000+ ランダム IOPS と比較すると遅すぎます。MySQL の変数が適切な値に設定され、テーブル サイズがキャッシュ サイズよりも大きくない場合、パフォーマンスは非常に良好ですが、テーブルがその制限を超えて大きくなると、縮退が発生します。そのため、テーブル構造がどれだけ大きくなるかを把握していない限り、テーブル構造を過度に最適化しないでください。また、全体を通してシステムの制限をテストしてください。テーブルの分割が早すぎると、あまり利点が見られず、パフォーマンスがさらに低下する可能性があります。

ベンチマークはゲームと同じで、実際のケースを実際に表すことはできないため、ゲームのルールを規制する必要があります。最初のシナリオのパフォーマンスはメモリ キャッシュとディスクの読み取り/書き込み戦略に大きく依存するため、my.cnf の設定、特にバッファ変数に興味がありました。変数は次のとおりです。

  • table_definition_cache : この変数は、メモリに格納できるテーブル メタデータ (MyISAM にとっては .frm ファイル) の量を示します。1 つのテーブルが繰り返し開かれた場合は役に立ちませんが、このキャッシュにすべてのテーブルのメタデータを含めることができれば、多くのテーブル (この場合は 3000 テーブル) を開く必要がある場合に役立ちます。
  • table_open_cache : この変数は、MySQL がメモリ内に保持できる内部テーブル ハンドラーの数を示します。上記と同様に、テーブル コンテキストの切り替え速度が向上します。
  • key_buffer_size : MyISAM を使用していたため、この変数はパフォーマンスにおいて非常に重要な役割を果たします。MySQL が MyISAM テーブルに割り当てることができる最大メモリ領域サイズを設定します。MyISAM を主に使用する場合、推奨値はシステム メモリの 30% です。私が 30% を取った理由は、キャッシュするものが 2 つあるからです。1 つはインデックスで、もう 1 つは行データです。key_buffer_size はインデックスを表し、OS は行データ キャッシュ (ブロック I/O バッファー キャッシュ) を処理します。インデックス用に 30%、行データ用に 50%、table_*_cache、thread_cache、connection_cache などの残りのバッファ キャッシュ用に 20% を残します。両方のケースに影響し、マルチテーブルはさらに影響を受けます。
  • key_cache_block_size : この変数は、キャッシュ ブロックのサイズを設定します。これにより、I/O が無駄になり (ヘッド/テール オーバー リード)、リードアラウンド ライト (書き込み前にリード) が発生します。複数テーブルのシナリオでは、より多くのテーブル (ファイル) があるため、より多くの問題が発生する可能性があります。

また、SQL クエリがどのように記述されているか、MySQL への読み取り/書き込みに使用しているスレッドの数にも興味がありました。たとえば、1 つのテーブルへの順次書き込みは、順次書き込みのように感じられ、速度はランダム書き込みよりもはるかに高速です。3000 個のテーブルへの順次書き込みは、ランダム書き込みのように感じられ、逆に速度が遅くなる可能性があります。3000 個のテーブルが作成されたとき、3000 個の .MYI ファイルと 3000 個の .MYD ファイルがありました。これらはディスク上で連続していない可能性があります (ランダム I/O が発生します)。彼ら自身。これは、ディスクの読み取りにも当てはまります。しかし、あなたの場合、読み取りは書き込みよりもはるかに遅いです。おそらく、初めて行を選択する場合、書き込みはバッファリングされますが、読み取りはバッファリングされないためだと思います。また、1 つのテーブルから読み取る場合、MySQL は key_cache 全体を 1 回プリロードできます。また、次のブロックは連続しているため、OS も次のブロックを事前に読み取ることができます。しかし、マルチテーブルでは、MySQL/OS は全体としてそれを行うことができません。より多くのクライアント スレッドを生成してクエリを発行できる場合は、両方のケースのパフォーマンスが近くなる可能性があります。

パーティションに関するあなたの最近の更新について、ええ、おそらく正しいと思います。「タイプ」によるパーティション分割は、「タイプ」ではなく主キーで順序付けられた SQL データをバッチ挿入するときのランダム I/O のように聞こえます。パーティション テーブル ハンドラ スイッチ。

于 2013-07-05T15:44:26.563 に答える