web-dev-qa-db-ja.com

10億行を処理してカウントするためのデータベース設計

リアルタイムのGPSデータを約5000 prのレートで受信します。分(4 TCPサーバーから)。各サーバーは単一の接続を使用してデータを挿入し、挿入と挿入の間にデータをバッファリングします。15分ごとに、サービスはこのデータをフェッチして処理します旅行が作成されたら、実際のGPSデータは通常、それほど重要ではありません。ユーザーが地図上でルートを見たい場合のみです。

問題は、データベースが挿入されるデータの速度に追いつくのに苦労しているように見えることです。負荷が増加すると、挿入時間が急激に増加し(> 30秒)、その結果、より多くのデータをバッファリングできるようになり、その結果、挿入が大きくなり、挿入時間が長くなります。

現在のデザイン、パフォーマンスを改善するために必要ないくつかのアイデア、いくつかの質問への回答、そして人々が持っている可能性のあるその他のヒントについて、コメントをいただければ幸いです。

現在のデザイン

現在、データは1週間を表すテーブルに分割されており、1年以上経過したデータはセカンダリデータベースにアーカイブされます。全体が編集可能なビューに結合され、挿入と読み取りの両方に使用されます。

テーブルデザイン

  • Id(PK、uniqueidentifier)
  • DeviceId(FK、int)
  • PersonId(FK、int)
  • VehicleId(FK、int)
  • TokenId(FK、int)
  • UtcTime(PK、datetime2(3))
  • 緯度(float)
  • 経度(float)
  • 速度(smallint)
  • 見出し(smallint)
  • 衛星(tinyint)
  • IOData(varbinary(100))
  • IgnitionState(tinyint)
  • UserInput(tinyint)
  • CreateTimeUtc(datetime2(3))

インデックス

  • DeviceId_CreateTimeUtc_Desc
  • DeviceId_UtcTime_Desc(クラスター)
  • PersonId_UtcTime_Desc
  • TokenId_UtcTime_Desc
  • VehicleId_UtcTime_Desc

現在、毎週、インデックスを含めて約10 GBを占めています。現在、メインデータベースには約300 GBのデータがあります。

メインデータベースのデータテーブルには、1つのファイルを持つ独自のファイルグループがありますが、メインデータベースの他のすべてのテーブルと同じディスク上にあります。セカンダリデータベースは別のディスクにありますが、同じマシン上にあります。

新しいテーブルパーティション(週)が使用されるときに、インデックスの再構築ジョブも毎週実行していると思います。縮小は行われません。

マシンは12 GBのメモリを搭載した8コアHPであり、メインデータベースを保持するディスクはRAID 10を実行しています。

アイデア

  • プライマリデータベースに保存されるデータの量を、たとえば最大1か月。少なくとも、データベースをバックアップ/復元用に管理しやすくしますが、これによりパフォーマンスの向上が見込めますか?
  • 現在のデータ用に2つのファイルをファイルグループに作成し、2つの異なる物理パーティションに配布する
  • 現在のデータを保持するマスタースレーブデータベースを作成し、挿入と読み取りが異なるデータベースで実行されるようにする
  • 現在のデータのファイルをSSDディスクに配置します(ミラーリングによりSSDディスクとのパフォーマンスに違いが生じますか?)

さらに情報が必要な場合はお知らせください。パフォーマンスに影響を与える恐ろしいほど多くの要因があり、おそらくそれを調整する多くの方法があります。

10
sondergard

毎分5000挿入は、毎秒約83挿入です。 1秒あたり400の物理行を挿入する5つのインデックス。ワークロードがメモリ内にある場合、これは最小のサーバーにさえ問題を提起しません。これが行ごとの挿入だったとしても、私が考えることができる最も非効率的な方法を使用しています。 1秒あたり83個の簡単なクエリは、CPUの観点からは興味がありません。

おそらく、あなたはディスクに縛られています。これを確認するには、待機統計またはSTATISTICS IO

クエリは多くの異なるページに触れるので、バッファプールにはそれらすべてのスペースがありません。これにより、ページの読み取りが頻繁に行われ、おそらくディスクの書き込みもランダムになります。

増え続けるキーのために、最後に物理的にのみ挿入するテーブルを想像してください。ワーキングセットは1ページ、つまり最後のページになります。これにより、シーケンシャルIO=が生成されます。また、レイジーライターまたはチェックポイントプロセスによって、テーブルの「終わり」がディスクに書き込まれます。

ランダムに配置された挿入を含むテーブルを想像してください(典型的な例:GUIDキー)。ここでは、挿入ごとにランダムなページがタッチされるため、allページがワーキングセットです。 IOはランダムです。これは、ワーキングセットに関しては最悪のケースです。

あなたは真ん中です。あなたのインデックスは構造(SomeValue, SequentialDateTime)。最初のコンポーネントは、2番目のコンポーネントによって提供される連続性を部分的にランダム化します。 "SomeValue"にはかなりの数の可能な値があると思います。これにより、インデックスにランダムに配置された多くの挿入ポイントができます。

あなたはデータが毎週10GBのテーブルに分割されると言います。これは、ワーキングセットが10GBに制限されているため、良い出発点です(実行する読み取りを無視して)。ただし、12 GBのサーバーメモリでは、関連するすべてのページがメモリ内にとどまることはほとんどありません。

毎週の「パーティション」のサイズを減らすか、サーバーのメモリを少し増やすことができる場合は、おそらく問題ありません。

週の初めの挿入は、終わりの挿入よりも速いと思います。特定のデータサイズでベンチマークを実行し、パフォーマンスタンクが表示されるまでサーバーのメモリを徐々に減らして、開発サーバーでこの理論をテストできます。

これで、すべての読み取りと書き込みがメモリに収まる場合でも、ランダムなダーティページフラッシュIOが残っている可能性があります。それを取り除く唯一の方法は、インデックス内の同じ場所にある位置に書き込むことです。インデックスを変換して(さらに)順次キーを使用できるようにすることができれば、非常に役立ちます。

簡単な解決策として、クライアントとメインテーブルの間にバッファリングレイヤーを追加します。ステージングテーブルに15分の書き込みを蓄積し、定期的にフラッシュする可能性があります。これにより、負荷の急上昇を取り除き、より効率的な計画を使用して大きなテーブルに書き込みます。

8
usr