あなたのアプリケーションは私のものと同じ特徴を持っているようです。この問題を効率的に解決するために、MySQL カスタム ストレージ エンジンを作成しました。ここに記載されています
データが、20 バイト (1 日あたり 1 つのエンティティの行) の 3650 行 (1 日あたり 1 行) を含む 2M の固定長エントリ (エンティティごとに 1 つ) の配列としてディスク上に配置されていると想像してください。
読み取りパターンは 1 つのエンティティを読み取ります。ディスク上で連続しているため、シークが 1 回 (約 8 ミリ秒) かかり、3650x20 = 約 80K がおそらく 100MB/秒で読み取られます。パターン。
更新では、ディスク上の 2M の異なる場所に 20 バイトを書き込む必要があります。最も単純なケースでは、それぞれ約 8 ミリ秒かかる 2M のシークが必要になるため、2M*8ms = 4.5 時間かかります。データを 4 つの「raid0」ディスクに分散すると、1.125 時間かかることがあります。
ただし、これらの場所は 80K しか離れていません。つまり、16MB ブロック (通常のディスク キャッシュ サイズ) 内にそのような場所が 200 あるため、最大 200 倍高速に動作できます。(1 分) 現実は 2 つの間のどこかにあります。
私のストレージ エンジンは、固定長配列よりも少し汎用性がありますが、そのような哲学に基づいて動作します。
私が説明したことを正確にコーディングできます。コードを MySQL プラガブル ストレージ エンジンに入れるということは、MySQL を使用して、さまざまなレポート ジェネレーターなどでデータをクエリできることを意味します。
ちなみに、保存された行から日付とエンティティ ID を削除することができ (これらは配列インデックスであるため)、一意の ID である可能性があります。 2 つの値を 3 バイトの int として格納します。保存された行は 6 バイトで、16M あたり 700 回の更新があるため、挿入が高速になり、ファイルが小さくなります。
フラットファイルと比較して編集
コメントは一般的にフラットファイルを支持していることに気付きました。ディレクトリはファイル システムによって実装された単なるインデックスであり、通常は比較的少数の比較的大きなアイテム用に最適化されていることを忘れないでください。ファイルへのアクセスは一般に最適化されているため、比較的少数のファイルが開かれていることが想定され、開いているファイルと閉じているファイル、および開いている各ファイルのオーバーヘッドが比較的高くなります。これらの「相対的」はすべて、データベースの通常の使用に関連しています。
ファイル システム名をエンティティ ID のインデックスとして使用することは、1 から 2Million までの非スパース整数であると考えていますが、直感に反しています。たとえば、プログラミングでは、ハッシュテーブルではなく配列を使用します。また、単に配列の実際の操作である可能性のある高価なアクセスパスに対して、必然的に大量のオーバーヘッドが発生します。
したがって、フラット ファイルを使用する場合は、フラット ファイルを1 つだけ使用してインデックスを作成してみませんか?
パフォーマンスの編集
このアプリケーションのパフォーマンスは、ディスクのシーク時間に支配されます。上で行った計算により、実行できる最善の方法が決まりました (ただし、SELECT を遅くすることで INSERT を速くすることはできますが、両方を改善することはできません)。データベース、フラット ファイル、または 1 つのフラット ファイルのいずれを使用するかは問題ではありませんが、実際には必要のないシークを追加して、さらに速度を低下させることができます。たとえば、インデックス作成 (ファイル システム インデックスかデータベース インデックスかに関係なく) は、「配列ルックアップ」と比較して余分な I/O を引き起こし、速度が低下します。
ベンチマーク測定の編集
あなたのテーブルに非常によく似た (またはあなたのパーティションの 1 つとほぼ同じ) テーブルがあります。2M (あなたの 32 分の 1) ではなく、64K のエンティティと 2788 の「日」でした。テーブルは、同じ INSERT 順序で作成され、同じインデックス (entity_id,day) を持ちます。1 つのエンティティに対する SELECT は、2788 日を検査するのに 20.3 秒かかります。これは、予想どおり、1 秒あたり約 130 シークです (平均シーク時間 8 ミリ秒のディスク上)。SELECT 時間は日数に比例し、エンティティの数にはあまり依存しません。(シーク時間が速いディスクでは高速になります。RAID0 で SATA2 のペアを使用していますが、大きな違いはありません)。
テーブルをエンティティの順序に並べ替える場合 ALTER TABLE x ORDER BY (ENTITY,DAY) 次に、同じ SELECT に 198 ミリ秒かかります (1 回のディスク アクセスで順序エンティティを読み取るため)。ただし、ALTER TABLE 操作が完了するまでに 13.98 日かかりました (182M 行の場合)。
測定値からわかることは他にもいくつかあります。 1. インデックス ファイルは、データ ファイルと同じ大きさになります。このサンプル テーブルでは 3GB です。つまり、(私のシステムでは)メモリ速度ではなくディスク速度でのすべてのインデックスです。
2.INSERT率は対数的に低下します。データ ファイルへの INSERT は線形ですが、インデックスへのキーの挿入はログです。1 億 8000 万レコードで、1 秒あたり 153 回の INSERT を取得していました。これは、シーク レートにも非常に近い値です。これは、MySQL がほぼすべての INSERT に対してリーフ インデックス ブロックを更新していることを示しています (エンティティにインデックスが付けられているが、日付順に挿入されているため、予想どおりです)。したがって、毎日 2M 行を挿入するのに 2M/153 秒 = 3.6 時間かかることになります。(システムまたはディスク全体のパーティションによって得られる効果で除算されます)。