3

我在 MongoDB 中存储了详细的性能数据,每个集合都是一种性能报告,每个文档都是阵列上端口在该时间点的测量值:

{
  "DateTime" : ISODate("2012-09-28T15:51:03.671Z"),
  "array_serial" : "12345",
  "Port Name" : "CL1-A",
  "metric" : 104.2
}

每个“array_serial”最多可以有 128 个不同的“端口名称”条目。

随着数据的老化,我希望能够在越来越长的时间跨度内对其进行平均:

  • 最多 1 周:分钟
  • 1 周至 1 个月:5 分钟
  • 1 - 3 个月:15 分钟

等等。这是我平均时间的方法,以便减少它们:

var resolution = 5; // How many minutes to average over     
var map = function(){
        var coeff = 1000 * 60 * resolution;
        var roundTime = new Date(Math.round(this.DateTime.getTime() / coeff) * coeff);
        emit(roundTime, { value : this.metric, count: 1 } );
 };

我将对 reduce 函数中的值和计数求和,并在 finalize 函数中获得平均值。

正如您所看到的,这将在省略“端口名称”值的时间内平均数据,并且我需要对每个“array_serial”上的每个“端口名称”随时间平均这些值。

那么如何在上面的 map 函数中包含端口名称呢?发射的键是否应该是我稍后拆分的复合“array_serial,PortName,DateTime”值?或者我应该使用查询功能来查询每个不同的串行、端口和时间?我是否正确地将这些数据存储在数据库中?

另外,据我所知,这些数据被保存到它自己的集合中,用这个平均数据替换集合中的数据的标准做法是什么?


这就是你的意思阿莎吗?因为它没有将四舍五入到下 5 分钟的文档分组(顺便说一句,我将 'DateTime' 更改为 'datetime'):

    $project: {
                "year" : { $year : "$datetime" },
                "month" : { $month : "$datetime" },
                "day" : { $dayOfMonth : "$datetime" },
                "hour" : { $hour : "$datetime" },
                "minute" : { $mod : [ {$minute : "$datetime"}, 5] },
                array_serial: 1,
                port_name: 1,
                port_number: 2,
                metric: 1
}

据我所知,“$mod”运算符将返回分钟的剩余部分除以五,对吗?

如果我可以让聚合框架而不是 mapreduce 来执行此操作,这将真的对我有帮助。

4

2 回答 2

3

以下是在聚合框架中如何做到这一点。我正在使用一个小的简化——我只对年、月和日期进行分组——在你的情况下,你需要添加小时和分钟来进行更细粒度的计算。如果您获得的数据样本中的点分布不均匀,您还可以选择是否进行加权平均。

project={"$project" : {
        "year" : {
            "$year" : "$DateTime"
        },
        "month" : {
            "$month" : "$DateTime"
        },
        "day" : {
            "$dayOfWeek" : "$DateTime"
        },
        "array_serial" : 1,
        "Port Name" : 1,
        "metric" : 1
    }
};
group={"$group" : {
        "_id" : {
            "a" : "$array_serial",
            "P" : "$Port Name",
            "y" : "$year",
            "m" : "$month",
                    "d" : "$day"
        },
        "avgMetric" : {
            "$avg" : "$metric"
        }
    }
};

db.metrics.aggregate([project, group]).result

我用一些随机样本数据运行它并得到了这种格式的东西:

[
    {
        "_id" : {
            "a" : "12345",
            "P" : "CL1-B",
            "y" : 2012,
            "m" : 9,
            "d" : 6
        },
        "avgMetric" : 100.8
    },
    {
        "_id" : {
            "a" : "12345",
            "P" : "CL1-B",
            "y" : 2012,
            "m" : 9,
            "d" : 7
        },
        "avgMetric" : 98
    },
    {
        "_id" : {
            "a" : "12345",
            "P" : "CL1-A",
            "y" : 2012,
            "m" : 9,
            "d" : 6
        },
        "avgMetric" : 105
    }
]

如您所见,这是每个 array_serial、端口名称、年/月/日组合的一个结果。您可以使用 $sort 将它们放入您想要从那里处理它们的顺序。

以下是您如何扩展项目步骤以包括小时和分钟,同时将分钟四舍五入以平均每五分钟:

{
    "$project" : {
        "year" : {
            "$year" : "$DateTime"
        },
        "month" : {
            "$month" : "$DateTime"
        },
        "day" : {
            "$dayOfWeek" : "$DateTime"
        },
        "hour" : {
            "$hour" : "$DateTime"
        },
        "fmin" : {
            "$subtract" : [
                {
                    "$minute" : "$DateTime"
                },
                {
                    "$mod" : [
                        {
                            "$minute" : "$DateTime"
                        },
                        5
                    ]
                }
            ]
        },
        "array_serial" : 1,
        "Port Name" : 1,
        "metric" : 1
    }
}

希望您能够将其扩展到您的特定数据和要求。

于 2012-10-09T20:26:46.873 回答
2

“用这个平均数据替换集合中的数据的标准做法是什么?”

标准做法是保留原始数据并单独存储所有派生数据。

在您的情况下,这意味着:

  • 不要删除原始数据
  • 使用另一个集合(在同一个 MongoDB 数据库中)来存储平均值
于 2012-09-30T15:33:38.310 回答