時系列データについて
内部的には、MongoDB は共通の metaField
値に基づいて時系列コレクション内のドキュメントをグループ化することで、時系列データを最適化します。有用な値を選択すると、ストレージの密度とクエリパフォーマンスが大幅に最適化されます。詳細については、「metaFields」を参照してください。
時系列データのプロパティ
時系列データには、他のデータ形式とは異なるいくつかの特性があります。
ドキュメントは順番に到着するため、追加するには頻繁に挿入操作を行う必要がある。
各ドキュメントはその時点を表す 1 つの点を表すため、更新操作がほとんどない。
広範な履歴レコードを保持することがアプリケーションに対してメリットがある場合、削除操作はまれに発生する。
データは、時間とそれが属する固有の時系列を識別する識別子(株式ティッカーなど)によってインデックス付けされる。
個々の時系列は常に増え続ける大量のドキュメントを必要とするため、データが大量にある。
これらの要因を考慮するために、MongoDB は各時系列のドキュメントをグループ化する特殊な列指向形式を使用します。これには、次の利点があります。
ストレージとインデックスのサイズを縮小
クエリ効率の向上
読み取り操作の I/O を削減
WiredTiger インメモリキャッシュの使用率が向上し、クエリ速度をさらに向上
時系列データの操作の簡素化
時系列コレクションと通常のコレクションの比較
通常のコレクションでは、データはディスクのブロックとして順番に保存され、書き込み速度が最適化されます。ただし、データポイントごとに 1 つのインデックスが必要であり、すぐに非常に大きくなります。また、ユーザーが 1 つの系列を検索できるように、時系列識別子とタイムスタンプ自体を含む2つ目のインデックスも必要です。このデータを読み取るには、ブロックに関連するドキュメントが 1 つしか含まれていない場合でも、MongoDB はそのブロックを含むすべてのデータベースブロックとディスクブロックを処理する必要があります。
このモデルは、CRUD 操作と頻繁な更新に最適化されています。銀行口座の残高は現在の状態を反映するだけでよいので、情報が変わると各口座名義人のドキュメントが更新されます。
これを時系列コレクションと比較します。時系列コレクションはデータを順番に書き込むため、最近のトランザクションをメモリに保持して、より高速に取得できます。データは順番に書き込まれ、ドキュメントはまとめて保存されるため、ドキュメントがメモリ内になくなった後、すべてのディスクブロックを読み取る必要はありません。データは metaField
によってインデックス化されるため、インデックスのサイズが大幅に小さくなります。
バケットの仕組み
時時系列コレクションを作成すると、MongoDB は自動的に system.buckets
システムコレクションを作成します。MongoDB は次の両方を持つドキュメントをグループ化します。
時系列を一意に識別する同一の
metaField
値。metaField
がオブジェクトまたは配列の場合、MongoDB は、すべてのオブジェクトフィールドまたは配列要素が一致する場合にのみグループ化する。timeField
近い値。時系列コレクションのgranularity
、bucketMaxSpanSeconds
、およびbucketRoundingSeconds
パラメータは、各バケットの対象となる期間を制御する。詳細については、「時系列データの粒度の設定」を参照。
たとえば、粒度が seconds
の場合、MongoDB は同じ時間以内にドキュメントをバケット化します。バケットに metaField
の値が sensorA
で timeField
の値が 2024-08-01T18:23:21Z
のドキュメントが含まれている場合、metaField
が sensorB
の受信ドキュメントは時間に関係なく別のバケットに入ります。sensorA
から受信したドキュメントは、その timeField
が 2024-08-01T18:00:00Z
と 2024-08-01T18:59:59Z
の間にある場合にのみ同じバケットに入ります。
2023-03-27T16:24:35Z
という時間が設定されたドキュメントが既存バケットに収まらない場合、MongoDB は最小時間が2023-03-27T16:00:00Z
で、最大時間が2023-03-27T19:59:59Z
の新しいバケットを作成します。
注意
時系列コレクションの粒度を変更することはできますが、バケットの対象範囲を分から時間に延長するなど、より細かい測定値から粗い測定値に変更する場合に限られます。これによりコレクションのビュー定義は更新されますが、既存のバケットへのデータの保存方法は変わりません。
metaField がバケットに与える影響
metaField
値はグループ文書と完全に一致する必要があるため、時系列コレクションのバケット数は一意の metaField
値の数によって異なります。metaField
値がきめ細かくなったり変化したりするコレクションでは、密度がまばらで存続期間の短いバケットが多数生成されます。これにより、ストレージが低下し、クエリ効率が低下します。
たとえば、次のドキュメントでは、特定の気象センサーからのデータを簡単にクエリできるため、metadata
は metaField
の適切な選択肢です。これらのフィールドを使用して、MongoDB は 1 つのセンサーからの読み取り値を一緒にバケット化します。
{ timestamp: ISODate("2021-05-18T00:00:00.000Z"), metadata: { sensorId: 5578, type: 'temperature' }, temp: 12, _id: ObjectId("62f11bbf1e52f124b84479ad") }
バケットカタログ
バケットカタログは、WiredTiger の専用メモリ内キャッシュです。バケットを追跡してレイテンシを最小化し、同時書き込みを調整します。
カタログには、オープンバケットごとに metaField
、アクティブなライター、対象期間、ドキュメント数、サイズ、最近の操作などの情報が記載されています。MongoDB は、metaField
が異なるドキュメントに対して個別のバケットを作成するため、通常、複数のバケットが同時に開かれます。
競合状態による不整合を避けるため、競合する操作が実行されると、バケットを閉じてバケットカタログから削除される場合があります。mongod
を再起動すると、すべてのバケットが閉じられ、バケットカタログがリセットされます。
作成
MongoDB は、受信ドキュメントに適したバケットがない場合は新しいバケットを作成します。これは、次のいずれかに当てはまる場合に発生します。
ドキュメント
metaField
が、アクティブなバケットに一致しない。ドキュメントタイムスタンプが、すべてのアクティブバケットの範囲外。
ドキュメントが、すべてのアクティブバケットの残りのサイズまたはドキュメント制限を超えている。
新しいバケットの開始タイムスタンプは、コレクションの粒度に応じて切り捨てられます。これは、タイムスタンプの順序が異なるドキュメントが連続して到着した場合に対応します。
停止
MongoDB は、次のいずれかの状況でバケットを閉じます。
受信ドキュメントのタイムスタンプがバケットの範囲外であることから、対象期間を過ぎて時間が前後に移動した。これらの境界は、コレクションの粒度設定によって決まる。
バケットがドキュメント制限に達した(デフォルト: 1000)。
バケットがストレージサイズ制限を超えた。これは、次の場合に発生する。
サイズが許可最大値(デフォルト: 125 KinB)を超えている。
ドキュメント数が最小数(デフォルト: 10)かつサイズが 12 MiB 未満。
これは、データがより少なく大きなドキュメントで構成されている場合にパフォーマンスを最適化する設定の内部制限です。
アクティブなバケットのセットが許可されたストレージエンジンキャッシュサイズに収まらない。この情報は、
collStats
データベースコマンドを使用して確認できる。
バケットカタログが、許容される合計メモリ割り当て量を超えている(デフォルトでは、使用可能なシステムメモリの 2.5%)
チャンクの移行やアップデートなどの競合操作により、バケットのディスク上の状態が変更される。
mongod
再起動。これにより、すべてのバケットが閉じられる。
削除
MongoDB は、次の場合にバケットを削除します。
許可される最大タイムスタンプが、現在の時刻からコレクションの
expireAfterSeconds
パラメータを引いた値より小さい。これは、TTL コレクションの有効期間と同じ。delete
またはdb.collection.deleteMany()
コマンドにより、バケット内の最後のドキュメントが削除される。