時系列コレクションのベストプラクティス
項目一覧
このページでは、時系列コレクションのパフォーマンスとデータ使用量を向上させるためのベストプラクティスについて説明します。
挿入の最適化
時系列コレクションの挿入パフォーマンスを最適化するには、次のアクションを実行します。
ドキュメントのバッチ書込み (write)
複数のドキュメントを挿入する場合
ネットワーク上の往復を回避するには、複数の ステートメントではなく、単一の
insertMany()
insertOne()
ステートメントを使用します。可能であれば、シリーズごとに複数の測定値を含むバッチを作成します(メタデータによって定義)。
パフォーマンスを向上させるには、
ordered
パラメータをfalse
に設定します。
たとえば、 sensor A
とsensor B
の 2 つのセンサーがある場合、1 つのセンサーからの複数の測定値を含むバッチには、測定 1 件ごとに 1 回の挿入ではなく、1 回の挿入に対してコストが発生します。
次の操作では 6 つのドキュメントが挿入されますが、ドキュメントはセンサーの順序付けされているため、挿入コストは 2 回の挿入コスト(バッチあたり 1 回)のみになります。 パフォーマンスを向上させるために、 ordered
パラメータはfalse
に設定されています。
db.temperatures.insertMany( [ { "metadata": { "sensor": "sensorA" }, "timestamp": ISODate("2021-05-18T00:00:00.000Z"), "temperature": 10 }, { "metadata": { "sensor": "sensorA" }, "timestamp": ISODate("2021-05-19T00:00:00.000Z"), "temperature": 12 }, { "metadata": { "sensor": "sensorA" }, "timestamp": ISODate("2021-05-20T00:00:00.000Z"), "temperature": 13 }, { "metadata": { "sensor": "sensorB" }, "timestamp": ISODate("2021-05-18T00:00:00.000Z"), "temperature": 20 }, { "metadata": { "sensor": "sensorB" }, "timestamp": ISODate("2021-05-19T00:00:00.000Z"), "temperature": 25 }, { "metadata": { "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 }
クライアント数の増加
コレクションにデータを書き込むクライアントの数を増やすと、パフォーマンスが向上します。
圧縮の最適化
時系列コレクションのデータ圧縮を最適化するには、次のアクションを実行します。
ドキュメントから空のオブジェクトと配列を含むフィールドを省略
圧縮を最適化するために、データに空のオブジェクトまたは配列が含まれている場合は、ドキュメントから空のフィールドを省略します。
例えば、次のドキュメントについて考えてみます。
{ "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
フィールドと空の配列を持つフィールドとの間で、コンプレッサーのスキーマが変更されます。 スキーマの変更により、シーケンス内の 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] }
数値データを小数点以下の桁に丸めます
数値データをアプリケーションに必要な精度に丸めます。 数値データを小数点以下桁を丸めると、圧縮率が向上します。
クエリ パフォーマンスの最適化
適切なバケット粒度の設定
時系列コレクションを作成すると、MongoDB は受信した時系列データをバケットにグループ化します。 粒度を正確に設定することで、データの取り込み率に基づいてデータがバケット化される頻度を制御します。
MongoDB 6.3 以降では、カスタム バケット パラメーターbucketMaxSpanSeconds
とbucketRoundingSeconds
を使用してバケット境界を指定し、時系列データのバケット化方法をより正確に制御できます。
同じデータソースからの受信測定値間の時間範囲に最も一致するようにgranularity
またはカスタム バケット パラメーターを設定することでパフォーマンスを向上できます。 たとえば、数百万のセンサーから気象データを記録しているものの、各センサーからのデータは 5 分に 1 回のみ記録する場合は、 granularity
を"minutes"
に設定するか、カスタム バケット パラメータを300
(秒)に設定できます。
この場合、 granularity
をhours
に設定すると、最大 1 か月量のデータ取り込みイベントが単一のバケットにグループ化され、トラバース時間が長くなり、クエリが遅くなります。 これをseconds
に設定すると、ポーリング間隔ごとに複数のバケットが使用されます。その多くには 1 つのドキュメントのみが含まれる場合があります。
次の表は、特定のgranularity
値を使用する場合に 1 バケットのデータに含まれる最大時間間隔を示しています。
granularity | granularity バケット制限 |
---|---|
| 1 時間 |
| 24 時間 |
| 30 日間 |
セカンダリ インデックスの作成
クエリのパフォーマンスを向上させるには、一般的なクエリ パターンをサポートするために1 つ以上のセカンダリ インデックスをtimeField
とmetaField
に作成します。 バージョン 6.3 以降では、MongoDB はtimeField
とmetaField
にセカンダリ インデックスを自動的に作成します。
サブフィールドのクエリ メタフィールド
MongoDB は時系列コレクションの 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"}}])
これは、次のように機能します。