乐趣区

关于mongodb:技术干货-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 及以上版本,测量值字段反对的索引类型:

单字段索引

组合索引

2dsphere

局部条件索引

`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 对应的属性值

更新操作指定一个带有更新运算符表达式的更新文档(而不是替换文档或者更新的 pipeline 操作)

不反对 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 应用的较多。
在晋升本人外文的能力的同时,也心愿为社区做出渺小的奉献。

社区招募

为了让社区组委会成员和志愿者敌人们灵便参加,同时咱们为想要深度参加社区建设的搭档们开设了“招募通道”,如果您想要在社区外面结交气味相投的技术搭档,想要通过在社区积淀有价值的干货内容,想要一个展现本人的舞台,晋升本身的技术影响力,即刻退出社区奉献队伍~ 点击链接提交申请:
http://mongoingmongoing.mikec…

退出移动版