このページでは、時系列コレクションのパフォーマンスとデータ使用量を向上させるためのベストプラクティスについて説明します。
圧縮のベストプラクティス
時系列コレクションのデータ圧縮を最適化するには、次のアクションを実行します。
ドキュメントから空のオブジェクトと配列を含むフィールドを省略
データに空のオブジェクト、配列、または文字列が含まれている場合は、圧縮を最適化するために、ドキュメントから空のフィールドを取り除きます。
例えば、次のドキュメントについて考えてみます。
{ timestamp: ISODate("2020-01-23T00:00:00.441Z"), coordinates: [1.0, 2.0] }, { timestamp: ISODate("2020-01-23T00:00:10.441Z"), coordinates: [] }, { timestamp: ISODate("2020-01-23T00:00:20.441Z"), coordinates: [3.0, 5.0] }
coordinates 値が入力されたフィールドと、配列が空の coordinates フィールドは、コンプレッサーのスキーマが変更されます。スキーマの変更により、シーケンス内の 2 番目と 3 番目のドキュメントは非圧縮のままになります。
次のドキュメントに示すように、空の値を持つフィールドを取り除いて、圧縮を最適化します。
{ timestamp: ISODate("2020-01-23T00:00:00.441Z"), coordinates: [1.0, 2.0] }, { timestamp: ISODate("2020-01-23T00:00:10.441Z") }, { timestamp: ISODate("2020-01-23T00:00:20.441Z"), coordinates: [3.0, 5.0] }
数値データを小数点以下の桁に丸めます
数値データをアプリケーションに必要な精度に四捨五入します。数値データを四捨五入して小数点の桁数を減らすと、圧縮率が向上します。
標準の埋め込みフィールドのベストプラクティスの使用
埋め込みデータモデルがアプリケーションのニーズをサポートしている場合は、ネストされたフィールドを使用します。ネストされたフィールドの動作は、通常のコレクションと時系列コレクションで同じです。
例、次の例のような気象ドキュメントを含む時系列コレクションを考えてみましょう。
{ timestamp: ISODate("2024-06-17T10:00:00Z"), stationId: "ALPHA123", atmosphere: { temperature: 21.5, humidity: 68, pressure: 1013.25 }, wind: { speed: 7.2, direction: "NW" }, precipitation: 0.5, visibility: 10 }
MongoDB はネストされた各フィールドを個別に列圧縮を使用するため、フィールドを最上位にフラット化するのと同じ圧縮品質を提供します。結果として得られる圧縮は、ネストされたすべての フィールド を最上位のフィールドにフラット化するために発生する圧縮と同じです。
重要
列圧縮には一貫したネストされたフィールド順序が必要です。ただし、ドライバーを使用してデータを操作する場合は、順序に関する追加の考慮事項が必要になる場合があります。例、 Goドライバーの順序付けられた表現に関する考慮事項については、「 BSON Types 」を参照してください。詳しくは ドライバー のドキュメントを参照してください。
ワークロードの濃度が高い場合は、ネストされたオブジェクトをフラット化するとパフォーマンスが向上する可能性があります。例、次のドキュメントには前のドキュメントと同じデータが含まれていますが、フラット化された形式になっています。
{ timestamp: ISODate("2024-06-17T10:00:00Z"), stationId: "ALPHA123", atmosphere_temperature: 21.5, atmosphere_humidity: 68, atmosphere_pressure: 1013.25 wind_speed: 7.2, wind_direction: "NW" precipitation: 0.5, visibility: 10 }
注意
アプリケーションに最も自然なデータモデルを使用します 。 MongoDBでは、ネストされた構造によってパフォーマンス上の重大な問題が発生する場合にのみ、ドキュメントをフラット化することをお勧めします。
挿入のベストプラクティス
時系列コレクションの挿入パフォーマンスを最適化するには、以下の操作を実行します。
ドキュメントのバッチ書込み (write)
複数のドキュメントを挿入する場合
ネットワーク上の往復を回避するには、複数の ステートメントではなく、単一の
insertMany()insertOne()ステートメントを使用します。可能であれば、同じバッチに同一の
metaField値を含むデータを挿入します。orderedパラメータをfalseに設定します。
たとえば、sensor A と sensor B の 2 つの metaField 値に対応する 2 つのセンサーがある場合、1 つのセンサーからの複数の測定値を含むバッチでは、測定ごとに 1 回の挿入ではなく、1 回の挿入のコストが発生します。
次の操作では 6 つのドキュメントが挿入されますが、ドキュメントはセンサー順に並べられるため、挿入コストは 2 回(metaField 値ごとに1回)しかかかりません。パフォーマンスを向上させるために、ordered パラメータは false に設定されています。
db.temperatures.insertMany( [ { metaField: { sensor: "sensorA" }, timestamp: ISODate("2021-05-18T00:00:00.000Z"), temperature: 10 }, { metaField: { sensor: "sensorA" }, timestamp: ISODate("2021-05-19T00:00:00.000Z"), temperature: 12 }, { metaField: { sensor: "sensorA" }, timestamp: ISODate("2021-05-20T00:00:00.000Z"), temperature: 13 }, { metaField: { sensor: "sensorB" }, timestamp: ISODate("2021-05-18T00:00:00.000Z"), temperature: 20 }, { metaField: { sensor: "sensorB" }, timestamp: ISODate("2021-05-19T00:00:00.000Z"), temperature: 25 }, { metadField: { sensor: "sensorB" }, timestamp: ISODate("2021-05-20T00:00:00.000Z"), temperature: 26 } ], { "ordered": false } )
ドキュメントでの一貫したフィールド順序の使用
ドキュメントで一貫したフィールド順序を使用すると、挿入と圧縮のパフォーマンスが向上します。
注意
圧縮には一貫したネストされたフィールド順序が必要です。ネストされたフィールドによる圧縮の詳細については、「 ネストされたフィールドのベストプラクティス 」を参照してください。
例、次のドキュメントを挿入すると、すべてが同じフィールド順序を持つ次のドキュメントを挿入すると、最適なパフォーマンスが得られます。
{ _id: ObjectId("6250a0ef02a1877734a9df57"), timestamp: ISODate("2020-01-23T00:00:00.441Z"), name: "sensor1", range: 1 }, { _id: ObjectId("6560a0ef02a1877734a9df66"), timestamp: ISODate("2020-01-23T01:00:00.441Z"), name: "sensor1", range: 5 }
対照的に次のドキュメントは、フィールドの順序が異なるため、最適なパフォーマンスを実現しません。
{ range: 1, _id: ObjectId("6250a0ef02a1877734a9df57"), name: "sensor1", timestamp: ISODate("2020-01-23T00:00:00.441Z") }, { _id: ObjectId("6560a0ef02a1877734a9df66"), name: "sensor1", timestamp: ISODate("2020-01-23T01:00:00.441Z"), range: 5 }
クライアント数の増加
コレクションにデータを書き込むクライアントの数を増やすと、パフォーマンスが向上します。
シャーディングのベストプラクティス
時系列コレクションのシャーディングを最適化するには、次のアクションを実行します。
metaField をシャードキーとして使用する
metaField を使用してコレクションをシャードすると、時系列コレクションのシャードキーとして十分な濃度が得られます。
注意
MongoDB 8.0 以降、時系列コレクションで timeField をシャードキーとして使用することは非推奨です。
クエリのベストプラクティス
時系列コレクションのクエリを最適化するには、次のアクションを実行します。
コレクションの作成時に戦略的な metaField を設定する
metaField の選択は、アプリケーションのクエリの最適化に最も大きな影響を与えます。
metaField の一部として、ほとんどまたはまったく変更されないフィールドを選択します。
可能であれば、metaField の一部として、フィルター式に一般的な識別子またはその他の安定値を選択します。
metaField の一部としてフィルタリングに使用されていないフィールドを選択することは避けてください。 代わりに、それらのフィールドを測定値として使用してください。
詳細については、「metaField の考慮事項」を参照してください。
適切なバケット粒度の設定
時系列コレクションを作成すると、MongoDB は受信した時系列データをバケットにグループ化します。 粒度を正確に設定することで、データの取り込み率に基づいてデータがバケット化される頻度を制御します。
MongoDB 6.3 以降では、カスタム バケット パラメーターbucketMaxSpanSecondsとbucketRoundingSecondsを使用してバケット境界を指定し、時系列データのバケット化方法をより正確に制御できます。
同じデータソースからの受信測定値間の時間範囲に最も一致するようにgranularityまたはカスタム バケット パラメーターを設定することでパフォーマンスを向上できます。 たとえば、数百万のセンサーから気象データを記録しているものの、各センサーからのデータは 5 分に 1 回のみ記録する場合は、 granularityを"minutes"に設定するか、カスタム バケット パラメータを300 (秒)に設定できます。
この場合、 granularityをhoursに設定すると、最大 1 か月量のデータ取り込みイベントが単一のバケットにグループ化され、トラバース時間が長くなり、クエリが遅くなります。 これをsecondsに設定すると、ポーリング間隔ごとに複数のバケットが使用されます。その多くには 1 つのドキュメントのみが含まれる場合があります。
次の表は、特定のgranularity値を使用する場合に 1 バケットのデータに含まれる最大時間間隔を示しています。
granularity | granularity バケット制限 |
|---|---|
| 1 時間 |
| 24 時間 |
| 30 日間 |
Tip
セカンダリ インデックスの作成
クエリのパフォーマンスを向上させるには、一般的なクエリ パターンをサポートするために1 つ以上のセカンダリ インデックスをtimeFieldとmetaFieldに作成します。 バージョン 6.3 以降では、MongoDB はtimeFieldとmetaFieldにセカンダリ インデックスを自動的に作成します。
追加インデックスのベストプラクティス
フィルタリングと等価性のために metaField インデックスを使用する。
範囲クエリには timeField やその他のインデックス付きフィールドを使用する。
一般的なインデックス戦略は、時系列コレクションにも適用される。詳細については、「インデックス戦略」を参照。
サブフィールドでの metaField のクエリ
MongoDB は時系列コレクションの metaField の順序を再配置します。これにより、サーバーはアプリケーションとは異なるフィールド順序でデータを保存する場合があります。metaField がオブジェクトの場合、metaField に対するクエリでは結果に一貫性がありません。これは、metaField の順序がサーバーとアプリケーションによって異なる場合があるためです。時系列 metaField でのクエリを最適化するには、metaField 全体ではなく、スカラーのサブフィールドで metaField をクエリします。
次の例では、時系列コレクションを作成しています。
db.weather.insertMany( [ { metaField: { sensorId: 5578, type: "temperature" }, timestamp: ISODate( "2021-05-18T00:00:00.000Z" ), temp: 12 }, { metaField: { sensorId: 5578, type: "temperature" }, timestamp: ISODate( "2021-05-18T04:00:00.000Z" ), temp: 11 } ] )
sensorIdとtypeのスカラー サブフィールドに対する次のクエリは、クエリ条件に一致する最初のドキュメントを返します。
db.weather.findOne( { "metaField.sensorId": 5578, "metaField.type": "temperature" } )
出力例:
{ _id: ObjectId("6572371964eb5ad43054d572"), metaField: { sensorId: 5578, type: 'temperature' }, timestamp: ISODate( "2021-05-18T00:00:00.000Z" ), temp: 12 }
Distinct() の代わりに $group を使用する
時系列コレクションは一意のデータ構造であるため、MongoDB は個別の値に対して効率的にインデックスを作成できません。時系列コレクションで distinct コマンドまたは db.collection.distinct() ヘルパーメソッドを使用しないでください。代わりに、$group 集計を使用して、ドキュメントを個別の値でグループ化します。
たとえば、meta.project = 10 であるドキュメントに対して meta.type の値を個別でクエリするには、次のようにします。
db.foo.distinct("meta.type", {"meta.project": 10})
次を使用します。
db.foo.createIndex({"meta.project":1, "meta.type":1}) db.foo.aggregate([{$match: {"meta.project": 10}}, {$group: {_id: "$meta.type"}}])
これは、次のように機能します。