2014-06-07 74 views
0

我正在使用MongoDB来存储每15秒捕获一次的服务器统计信息(因此每个服务器每分钟插入4行),并试图将此数据绘制到图形之间,以获取所有数据一定的时间戳。MongoDB按时间间隔聚合PHP

例如,下面的查询可以使用:

$tbl->find(
    array(
    "timestamp" => array('$gte' => '1396310400', '$lte' => '1396915200'), 
    "service" => 'a715feac3db42f54edbc50ef6fa057b3' 
), 
    array("timestamp" => 1, "system" => 1) 
); 

哪吐了一堆行看起来像这样:

Array 
(
    [53933ad8532965621d97dd3b] => Array 
     (
      [_id] => MongoId Object 
       (
        [$id] => 53933ad8532965621d97dd3b 
       ) 

      [system] => Array 
       (
        [load] => 0.55 
        [uptime] => 1171204.47 
        [processes] => 222 
       ) 

      [timestamp] => 1396310403 
     ) 

) 

这工作适合于小数据范围,如我可以将这些数据直接传递给Flot或HighCharts,让它可以自行优化时间尺度。但是,这对于大型数据集不适用(例如,查询一个月)。

我想要做的是按小时(或15分钟)对数据进行分组,并返回给定时间段内的平均值(在本例中为我绘制的system.load)。

我知道聚合函数是我需要使用的,但尽管我尽了最大努力,但我一直无法得到这个工作。

现在我让PHP完成所有工作(按时间戳分组结果并计算平均值),但速度非常慢,我知道MongoDB会更好地处理它。

任何有识之士将不胜感激!

编辑: 我一直努力遵循此处发布答案,但我仍然在努力 - MongoDB Aggregation PHP, Group by Hours

回答

1

我看你在你的问题的顶部初始查询,立刻告诉我,你的“时间戳“值实际上是字符串。所以毫无疑问,当你阅读这些信息并进行“手动聚合”时,实际上是在将这些值,可能还有其他值转换为可以操作,求和和求平均值的类型。

因此,这里的第一部分是修复您的数据,它看起来像来自日志记录源,但您从未转换过这些值。我正在考虑合理的可能性,这不仅仅是时间戳值,还可能是系统下的指标。

这使您可以选择如何存储时间戳。您可以将它保留为时间戳编号,因为它目前是字符串形式,或者您可以选择转换为​​。第一个将是一个简单的整数转换并保存,另一个应该能够提供给驱动程序支持的Date类型,并再次保存数据。

完成此操作后,您就可以愉快地使用聚合功能。因此,作为一个例子,如果你选择保留这是一个数字,那么你只需要申请日期的功能,以获得组边界:

db.collection.aggregate([ 

    // Match documents on the range you want 
    { "$match": { 
     "timestamp": { 
      "$gte": 1396310400, "$lte": 1396915200 
     }, 
     "service": "a715feac3db42f54edbc50ef6fa057b3" 
    }}, 

    // Group on the time intervals, 15 minutes here 
    { "$group": { 
     "_id": { 
      "service": "$service", 
      "time": { 
       "$subtract": [ 
        "$timestamp", 
        { "$mod": [ "$timestamp", 60 * 15 ] } 
       ] 
      } 
     }, 
     "load": { "$avg": "$system.load" } 
    }}, 

    // Project to the output form you want 
    { "$project": {  
     "service": "$_id.service", 
     "time" : "$_id.time", 
     "load": 1 
    }} 
]) 

或者是PHP的具体

$tbl->aggregate(array(
    array(  
     '$match' => array(
      'timestamp' => array(
       '$gte' => 1396310400, '$lte' => 1396915200 
      ), 
      'service' => 'a715feac3db42f54edbc50ef6fa057b3' 
     ) 
    ), 
    array(
     '$group' => array(
      '_id' => array(
       'service' => '$service', 
       'time' => array(
        '$subtract' => array(
         '$timestamp', 
         array('$mod' => array('$timestamp', 60 * 15)) 
        ) 
       ) 
      ), 
      'load' => array('$avg' => '$system.load') 
     ) 
    ), 
    array(
     '$project' => array(
      'service' => '$_id.service', 
      'time' => '$_id.time', 
      'load' => 1 
     ) 
    ) 
)) 

否则,如果您选择转换为BSON日期,那么你可以使用date aggregation operators代替:

db.collection.aggregate([ 
    { "$match": { 
     "timestamp": { 
      "$gte": new Date("2014-04-01"), "$lte": new Date("2014-04-08") 
     }, 
     "service": "a715feac3db42f54edbc50ef6fa057b3" 
    }}, 
    { "$group": { 
     "service": "$service", 
     "time": { 
      "dayOfYear": { "$dayOfYear": "$timestamp" }, 
      "hour": { "$hour": "$timestamp" }, 
      "minute": { 
       "$subtract": [ 
        { "$minute": "$timestamp" }, 
        { 
         "$mod": [ 
          { "$minute": "$timestamp" }, 
          15 
         ] 
        } 
       ] 
      } 
     }, 
     "load": { "$avg": "$system.load" } 
    }}, 
    { "$project": { 
     "service": "$_id.service", 
     "time": "$_id.time", 
     "load": 1 
    }} 
]) 

因此,有你有date aggregation operators的帮助下,打破了大的部分你有,并仍然使用相同的模运算,以获得区间值。

如果你还是喜欢日期的功能方法,你仍然可以使用日期对象从另一个减去一个日期对象将是划时代的时间戳值的结果做到这一点。因此,移动BSON日期为纪元时间戳仅仅是一个事:

{ 
    "$subtract": [ 
     "$dateObjectField", 
     new Date("1970-01-01") 
    ] 
} 

你传递给管道因此,任何“日期”的价值观在这里你可以施放使用驱动程序的原生类​​型的方法,这将是当请求发送到MongoDB时序列化正确。另一个优点是,当你阅读它们时也是如此,所以在客户端处理中不再需要转换。

+0

尼尔您好,感谢的全面的答案。我会在接下来的20分钟左右执行此操作,并会报告回来。 我期待改变时间戳字段到BSON原生格式,我只是还没有想通做到这一点还没有最好的方式。 –

+0

您的答案完美无缺,我只需要纠正':' ' array('$ mod':array('$ timestamp',60 * 15))' 现在,数据存储在MySQL所以我的测试数据,我写道,在从MySQL时选择数据的批次,并插入行到MongoDB中的脚本。这可能是出于这个问题的范围,但有什么具体的我需要做的,我现在的时间戳格式(UNIX时间戳)转换为蒙戈的可用的格式? –

+0

@LeeBrooks有关于MongoDB驱动程序的文档,包括[MongoDate](http://www.php.net//manual/en/class.mongodate.php)class –