Docs 菜单
Docs 主页
/
MongoDB Manual
/

时间序列集合最佳实践

在此页面上

  • 压缩最佳实践
  • 省略文档中包含空对象和数组的字段
  • 将数值数据四舍五入到小数点后几位
  • 插入最佳实践
  • 批量文档写入
  • 在文档中使用一致的字段顺序
  • 增加客端户数量
  • 分片最佳实践
  • 使用 metaField 作为分片键
  • 查询最佳实践
  • 在创建集合时设置策略 metaField
  • 设置相应的桶粒度
  • 创建二级索引
  • 其他索引最佳实践
  • 在子字段上查询 metaField
  • 使用 $group 而不是 Distinct()

本页介绍了改善时间序列集合性能和数据使用情况的最佳实践。

要优化时间序列集合的数据压缩,请执行以下操作:

如果数据包含空对象、数组或字符串,请从文档中省略空字段以优化压缩。

例如,考虑以下文档:

{
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 字段会导致压缩器的模式发生更改。模式更改会导致序列中的第二个和第三个文档保持未压缩状态。

通过省略具有空值的字段来优化压缩,如以下文档所示:

{
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]
}

将数值数据四舍五入到应用程序所需的精度。将数值数据四舍五入到更少的小数位可提高压缩率。

要优化时间序列集合的插入性能,请执行以下操作:

插入多个文档时:

  • 为避免网络往返,请使用单个 insertMany() 语句,而不是多个 insertOne() 语句。

  • 如果可能,在同一批次中插入包含相同 metaField 值的数据。

  • ordered参数设置为false

例如,如果您有两个传感器对应于两个 metaField 值(sensor Asensor B),则一批包含来自单个传感器的多个测量值的传感器将产生一次插入的成本,而不是每次测量一次插入的成本。

以下操作会插入六个文档,但仅产生两次插入的成本(每 metaField 值一次),因为文档是按传感器排序的。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 对集合进行分片可提供足够的关联基数作为时间序列集合的分片键。

注意

从 MongoDB 8.0 开始,不推荐使用 timeField 作为时间序列集合中的分片键。

要优化时间序列集合上的查询,请执行以下操作:

您所选择的 metaField 对优化应用程序中的查询具有最大的影响。

  • 选择很少更改或从不更改的字段作为 metaField 的一部分。

  • 如果可能,请选择过滤表达式中常用的标识符或其他稳定值作为 MetaField 的一部分。

  • 避免选择不用于过滤的字段作为 MetaField 的一部分。取而代之的是,请使用这些字段作为测量值。

有关更多信息,请参阅 metaField 注意事项

创建时间序列集合时,MongoDB 会将传入时间序列数据分组到存储桶中。通过精确设置粒度,您可以根据数据摄取率来控制数据的分组频率。

从 MongoDB 6.3 开始,可以使用自定义分桶参数 bucketMaxSpanSecondsbucketRoundingSeconds 来指定存储桶边界,并更准确地控制时间序列数据的分桶方式。

您可以将 granularity 或自定义分组参数设置为与同一数据源的传入测量值之间时间段的最佳匹配,从而提高性能。例如,如果您要记录数千个传感器的天气数据,但每个传感器每 5 分钟只记录一次数据,您可以将 granularity 设置为 "minutes",或者将自定义分组参数设置为 300(秒)。

在这种情况下,将 granularity 设置为 hours 会将最多一个月的数据导入事件分组到单个存储桶中,从而导致遍历时间更长且查询速度更慢。将其设置为 seconds 会导致每个轮询间隔有多个存储桶,其中许多可能只包含一个文档。

下表显示了使用给定 granularity 值时一个数据桶中包含的最长时间间隔:

granularity
granularity 存储桶限额
seconds
1 小时
minutes
24 小时
hours
30天

提示

另请参阅:

为提升查询性能,请在 timeFieldmetaField创建一个或多个二级索引,以支持常见查询模式。在版本 6.3 及更高版本中,MongoDB 会自动在 timeFieldmetaField 上创建二级索引。

  • 使用 metaField 索引进行过滤和相等操作。

  • 使用 timeField 和其他索引字段进行范围查询。

  • 一般索引策略也适用于时间序列集合。有关更多信息,请参阅索引策略。

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
}
] )

以下对 sensorIdtype 标量子字段的查询返回第一个与查询条件匹配的文档:

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
}

由于时间序列集合的独特数据结构, 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"}}])

具体操作如下:

  1. meta.projectmeta.type 上创建复合索引并支持聚合。

  2. $match 阶段过滤 meta.project = 10 的文档。

  3. $group 阶段使用meta.type作为分组键,为每个唯一值输出一个文档。

后退

添加二级索引