私の目標は、高速読み取りのために MySQL テーブルに約 6000 万行を保存することであり、適切に挿入も続行します。
製品設計では、これらの 6000 万行は自然に 3000 個のチャンクに分割できるため、1 つの 60M テーブルを 3000 個のテーブルに分割するテーブル シャーディング戦略を作成することにしました。
次のテストのために 300 万のデータを取得しました。
1 つのテーブルに 300 万行: この 300 万のデータの平均挿入時間は 80 秒で、1000 クエリ (各クエリはこの 300 万のデータ テーブルから 1000 行をフェッチする) ごとに約 10 秒かかります。
平均 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 つのパーティションすべてを頻繁かつランダムに開く必要があるため、テーブルのロックとロック解除に時間がかかりすぎましたか? それが理由でしょうか?