技術乾貨| MongoDB時間序列集合
名詞解釋 Glossary
bucket:帶有相同的元資料且在一段有限制的間 隔區間內的測量值組。
bucket collection :用於儲存時序型集合的底層的分組桶的系統集合。複製、分片和索引都是在桶級別上完成的。
measurement:帶有特定時間序列的K-V集合。
meta-data:時序序列裡很少隨時間變化的K-V對,同時可以用於識別整個時序序列。
time-series:一段間隔內的一系列測量值。
time-series collection:一種表示可寫的非物化的檢視的集合型別,它允許儲存和查詢多個時間序列,每個序列可以有不同的元資料。
MongoDB 在5.0中支援了新的 timeseries collection
型別的選項,該型別用於儲存時序型資料。 timeseries collection
提供了一組用於插入和查詢測量值的簡單介面,同時底層實際的資料是儲存在以 bucket
形式的集合中。
在建立 timeseries collection
時, timeField
欄位是最小必備的配置項。 metaField
是另一個可選的、可被指定的元資料欄位,它是用於在 bucket
中對測量值分組的依據。MongoDB通過提供 expireAfterSeconds
欄位選項,也支援了對測量值的過期機制。
在 mydb
資料庫中有個以 mytscoll
命名的 timeseries collection
,該集合在MongoDB內部的 catelog
(用於儲存集合或檢視的資訊)裡是由一個檢視和一個系統集合組成的。
-
mydb.mytscoll
是個檢視,它在MongoDB底層是用bucket collection
作為包含特定屬性的原始集合實現的:-
該檢視就是通過
aggregation
裡的$_internalUnpackBucket
來實現展開bucket
裡資料的。 - 該檢視是可寫的(僅支援插入)。同時每個被插入的文件必須包含時間欄位。
-
在查詢檢視時,它會隱式地展開底層在
bucket collection
中儲存的資料,然後返回原始的非bucket
形式的文件資料。
-
該檢視就是通過
-
該系統集合的名稱空間是
mydb.system.buckets.mytscoll
,它是用來儲存實際資料的。-
每一個在
bucket collection
裡的文件,都表示了一組區間間隔的時序型資料。 -
如果在建立
timeseries collection
時,定義了metaField
元資料欄位,那麼所有在bucket
裡的測量值都會有這個通用的元資料欄位。 -
除了時間範圍,
bucket
還限制了每個文件資料的總條數以及測量值的大小。
-
每一個在
Bucket Collection Schema
{
_id: <Object ID with time component equal to control.min.<time field>>,
control: {
// <Some statistics on the measurements such min/max values of data fields>
version: 1, // Version of bucket schema. Currently fixed at 1 since this is the
// first iteration of time-series collections.
min: {
<time field>: <time of first measurement in this bucket, rounded down based on granularity>,
<field0>: <minimum value of 'field0' across all measurements>,
<field1>: <maximum value of 'field1' across all measurements>,
...
},
max: {
<time field>: <time of last measurement in this bucket>,
<field0>: <maximum value of 'field0' across all measurements>,
<field1>: <maximum value of 'field1' across all measurements>,
...
},
closed: <bool> // Optional, signals the database that this document will not receive any
// additional measurements.
},
meta: <meta-data field (if specified at creation) value common to all measurements in this bucket>,
data: {
<time field>: {
'0', <time of first measurement>,
'1', <time of second measurement>,
...
'<n-1>': <time of n-th measurement>,
},
<field0>: {
'0', <value of 'field0' in first measurement>,
'1', <value of 'field0' in first measurement>,
...
},
<field1>: {
'0', <value of 'field1' in first measurement>,
'1', <value of 'field1' in first measurement>,
...
},
...
}
}
索引indexes
為了保證 timeseries collection
的查詢可以受益於索引掃描而不是全表掃描, timeseries collection
允許索引可以被建立在時間上,元資料上以及元資料的子屬性上。從MongoDB5.2開始,在 timeseries collection
也允許索引被建立在測量值上。使用者使用 createIndex
命令提供的索引規範被轉換為底層 buckets collection
的模式。
-
timeseries collection
與底層的buckets collection
之間的索引對映轉換關係細節,你可以參考timeseries_index_schema_conversion_functions.h
. -
在v5.2及以上版本的最新支援的索引型別,
timeseries collection
會儲存使用者原始的索引定義到變換後的索引定義上。當從底層的bucket collection
的索引對映到timeseries collections
的索引時,會返回使用者原始的索引定義。
當索引被建立後,可以通過 listIndexes
命令或 $indexStats
聚合計劃來檢查。 listIndexes
和 $indexStats
是作用於 timeseries collections
的,執行時,它們會在內部將底層的 bucket collection
的索引轉化成 timeseries
格式的索引,並返回。比如,當我們在元資料欄位中定義有 mm
的 timeseries collection
上執行 listIndexes
命令時,底層的 bucket collection
的 {meta:1}
索引,將會以 {mm:1}
格式返回。
dropIndex
和 collMod
(hidden: <bool>, expireAfterSeconds: <num>)
也同樣支援在 timeseries collection
上。
時間欄位上支援的索引型別:
元資料欄位和元資料子欄位支援的索引型別:
- 支援所有時間欄位上支援的索引型別
- v5.2及以上版本支援2d 索引
- v5.2及以上版本支援2dsphere 索引
- v5.2及以上版本支援 Partial索引
僅在v5.2及以上版本,測量值欄位支援的索引型別:
timeseries collections
上不支援的索引型別,包括 唯一索引以及 文字索引
。
桶目錄Catalog
為了保證高效地桶(分組)操作,我們在 BucketCatalog
裡維護了一組開啟的桶,你可以在 bucket_catalog.h
找到。在更高的級別,我們嘗試著把併發寫程式的寫操作分組合併為可以一起提交地批處理,以減少對底層文件的寫次數。寫程式會插入它的輸入批處理裡的每一個文件到 BucketCatalog
,然後 BucketCatalog
會返回一個 BucketCatalog::WriteBatch
的處理器。一旦完成上面那些插入操作後,寫程式就會檢查每個寫批處理。如果沒有其他的寫程式已經對批處理宣告提交的權利,那麼它會宣告權利,並會提交它的批處理。否則,寫程式將會稍後再提交處理。當它檢查完所有的批處理,寫程式將會等待其他的寫程式提交每個剩下的批處理。
在內部, BucketCatalog
維護一組對每個 bucket
文件的更新操作。當批處理被提交時,它會將這些插入轉換到成 buckets
的列格式,並確保任何 control
欄位的更新(例如 control.min
和 control.max
)。
當 bucket
文件在沒有通過 BucketCatalog
的情況下被更新時,寫程式就需要為有問題的文件或名稱空間去呼叫 BucketCatalog::clear
,這樣它就可以更新它的內部狀態,避免寫入任何可能破壞 bucket
格式的資料。這通常由OP觀察者處理,但可能需要通過其他地方去呼叫。
bucket
既可以通過手動設定選項 control.closed
標識來關閉,也可以在許多場景下通過 BucketCatalog
自動關閉。如果 BucketCatalog
使用了超出給定的閾值(可通過伺服器引數 timeseriesIdleBucketExpiryMemoryUsageThreshold
控制)的更多記憶體,此時它將會開始去關閉空閒的 bucket
。如果 bucket
是開啟的且它沒有任何未處於等待中未提交的測量值時,那麼它就會被視為空閒的 bucket
。在下面這些場下 BucketCatalog
也會關閉 bucket
: 如果它擁有超過最大閾值( timeseriesBucketMaxCount
)的測量值資料的數量;如果它擁有過大的資料量大小( timeseriesBucketMaxSize
);又或者一個新的測量值資料是否是會導致 bucket
在其最舊的時間戳和最新的時間戳之間跨度比允許的間隔更長的時間(當前硬編碼為一小時)。如果傳入的測量值在原理上與已經到達給定 bucket
的度量不相容,該 bucket
將被關閉,同時可以使用 numBucketsClosedDueToSchemaChange
度量進行跟蹤。
在第一次提交給定 bucket
的寫批處理時,就會生成新的完整的文件。後續的批處理提交中,我們只執行更新操作,不再生成新的完整的文件(因此稱為‘經典’更新),是直接建立 DocDiff
(“delta”或者v2的更新)。
粒度Granularity
timeseries collection
的 granularity
選項在集合建立的時候,可以被設定成 seconds
, minutes
或者 hours
。後期可通過 colMod
操作來修改這個選項從 seconds
到 minutes
或者從 minutes
到 hours
,除此之外的轉化修改目前都是不支援的。該引數想要表示在已給定的時序型測量資料之間的粗略的時間間隔,同時也用於調節其他內部引數對分組的影響。
單個 bucket
被允許的最大時間跨度,是由 granularity
選項控制,對於 seconds
,最大的時間跨度被設定成1小時,對於 minutes
就是24小時,對於 hours
就是30天。
當通過 BucketCatalog
開啟新的 bucket
時, _id
裡的時間戳就是等同於 control.min.<time field>
的值,該值是從第一個插入 bucket
的測量資料中根據 granularity
選項來向下近似舍入而得到的。對於 seconds
,它將向下舍入到最接近的分鐘,對於 minutes
,將向下舍入到最接近的小時,對於 hours
,它將向下舍入到最接近的日期。在閏秒和日曆中的其他不規則情況下,這種舍入可能並不完美,並且通常通過對自紀元以來的秒數進行基本模運算來完成,假設每分鐘 60 秒,每小時 60 分鐘,以及每天 24 小時。
更新和刪除
timeseries collection
支援符合以下限制的刪除語句:
metaField
同時更新滿足上面同樣的條件,另外遵循:
metaField upsert:true
這些更新與刪除的執行都會被轉換成相對應的底層的 bucket collection
的更新或刪除操作。特別是,對於查詢和更新文件,我們會使用真正的欄位 meta
替換集合的 metaField
。(參見 Bucket 集合規範
)
例如,對於一個使用 metaField: "tag"
建立的 timeseries
集合 db.ts
,考慮一個對這個集合的更新操作,其查詢語句是 {"tag.tag.a": "a"}
,同時更新文件語句是 {$set: {"tag.tag.a": "A"}
,
$rename: {"tag.tag.b": "tag.tag.c"}}
。這個更新操作在 db.system.buckets.ts
上會被轉換成,查詢語句是 {"meta.tag.a": "a"}
,更新語句是 {$set: {"meta.tag.a": "A"}
,
$rename: {"meta.tag.b": "meta.tag.c"}}
。然後這個轉換後的更新語句就可以像普通的更新操作一樣執行。上面這些轉換流程也適用於刪除操作。
參考文獻References
MongoDB Blog: Time Series Data and MongoDB: Part 2 – Schema Design Best Practices
關於作者:黃璜
目前就職於上海DerbySoft,主要從事基礎架構中業務流程設計及研發的工作,平時工作中MongoDB使用的較多。
在提升自己外文的能力的同時,也希望為社群做出微小的貢獻。
社群招募
為了讓社群組委會成員和志願者朋友們靈活參與,同時我們為想要深度參與社群建設的夥伴們開設了“招募通道”,如果您想要在社群裡面結交志同道合的技術夥伴,想要通過在社群沉澱有價值的乾貨內容,想要一個展示自己的舞臺,提升自身的技術影響力,即刻加入社群貢獻隊伍~ 點選連結提交申請:
- 直播分享| 騰訊雲 MongoDB 智慧診斷及效能優化實踐
- 技術乾貨| MongoDB 事務原理
- 行業案例| 千億級高併發 MongoDB 叢集在某頭部金融機構中的應用及效能優化實踐(上)
- 技術乾貨| MongoDB時間序列集合
- 技術乾貨| MongoDB如何查詢Null或不存在的欄位?
- 技術乾貨| MongoDB如何查詢Null或不存在的欄位?
- 技術乾貨| 如何在MongoDB中輕鬆使用GridFS?
- 【網路研討會】MongoDB Vs 效仿者:選擇MongoDB的理由
- MongoDB有哪些資料脫敏技術?
- 一文讀懂 | 驅動程式 API
- 記某核心MongoDB叢集索引優化實踐
- MongoDB常見問題解答:時間與時區
- MongoDB在資訊資源共享建設的應用實踐
- MongoDB在vivo評論中臺的應用案例
- mongo叢集分片update不均勻案例一則
- 【網路研討會】“專家面對面”-MongoDB模式設計-下午2點,與你相約!
- MongoDB find getmore操作慢問題排查
- multi-key索引和wildCard索引場景比較
- 分片叢集changeStream效能調優
- 【MongoDB白皮書】DIRT和複雜性的高成本