按月或日汇总的指标的MongoDB索引策略

时间:2012-12-04 20:56:25

标签: mongodb

我很好奇是否有任何最佳实践来索引按月/天汇总的集合的指标。

文档示例:

{
  track: {
    2012: { # year
      1: { # month
        page_views: ...,
        clicks: ...,
        visits: ...
      },
      5: {
        page_views: ...,
        clicks: ...,
        visits: ...
      },
      ...
  }
}

编辑:

因为有关如何改进文件的讨论和一些拆分的建议(我已经考虑过)。我将更新为什么要求是他们的方式。该文档用于跟踪用户。随着时间的推移跟踪他们的浏览量,访问量等。用户在文档上有其他数据。例如,有一个registeration_date。目标是能够说出“向我显示在X日期注册并且在A和B跟踪日期之间具有更多Z页面视图的用户”之类的内容。我没有能够提出一个没有嵌入的架构来促进这一点。

更新了文档示例:

{
  registration_date: ...,
  email: ...,
  track: {
    2012: { # year
      1: { # month
        page_views: ...,
        clicks: ...,
        visits: ...
      },
      5: {
        page_views: ...,
        clicks: ...,
        visits: ...
      },
      ...
  }
}

3 个答案:

答案 0 :(得分:3)

不幸的是,您的数据库架构非常索引 - 不友好。当您嵌套这样的对象时,您唯一的选择是在每个可能的年/月组合上创建索引。查询也很困难。如果您想要获得最好的三个月降序排序,那么您将很难在数据库上尝试这样做。

更好的选择是将年份和月份放入对象中,将对象放入数组中(因为索引可用于数组查找),并创建年,月和唯一字段的唯一复合索引周围的文件。

{
    name: "Some Unique Name",
    tracking:    [
        {year: 2011, month: 11, page_views: 235, clicks: 132, visits: 87 },
        {year: 2011, month: 12, page_views: 176, clicks: 122, visits: 67 },
        {year: 2012, month: 1, page_views: 53, clicks: 32, visits: 17 },
        {year: 2012, month: 2, page_views: 89, clicks: 72, visits: 67 },
        {year: 2012, month: 3, page_views: 99, clicks: 82, visits: 72 }
    ]
}

ensureIndex({name:1, tracking.year:1, tracking.month:1});

当您需要经常访问各个日,月或年的累积统计数据时,您可以将这些指标存储在各个子文档中:

    tracking_daily: [
        ...
        {year: 2012, month: 3, day: 1, ...  }, 
        {year: 2012, month: 3, day: 2, ...  }, 
        {year: 2012, month: 3, day: 3, ...  }, 
        {year: 2012, month: 3, day: 4, ...  }, 
        {year: 2012, month: 3, day: 5, ...  }, 
        {year: 2012, month: 3, day: 6, ...  }, 
        {year: 2012, month: 3, day: 7, ...  }, 
        {year: 2012, month: 3, day: 8, ...  }, 
        ...
    ],
    tracking_monthly: [
        ...
        {year: 2011, month: 11, ... },
        {year: 2011, month: 12, ... },
        {year: 2012, month: 1, ...  },
        {year: 2012, month: 2, ...  },
        {year: 2012, month: 3, ...  } 
        ...
    ],
    tracking_yearly:    [
        ...
        {year: 2011, ...  },
        {year: 2012, ...  }
    ]

答案 1 :(得分:1)

考虑到这一点后,我可能会建议一个架构。

我个人根本不会将子文档用于度量标准,因为我可以想象在一个公制时间内会有日期查询。

你还必须考虑从子文档中删除指标,特别是一个子文档,多年来,很容易导致客户端的大量处理,至少需要聚合框架;即便如此,我也不确定它能否在足够快的时间内做出真正的分析性查询,让你感到高兴。

省略子文档的另一个原因是将来与根文档的大小兼容。我在上一段中略微提到了这一点,并指出随着时间的推移,子文档可能变得相当大。

因此,为了将来的兼容性和查询速度,我不会广泛使用子文档。

通常情况下,从我自己的个人经验和许多关于此类模式的讨论中找到的一个好方法是将您的跟踪分配实际拆分为时间桶集合,因此您将按每日,每月和每年的统计数据收集;共创建3个系列。

在个人情况下,我个人也需要一个相对平坦的文档来确保在优化良好的索引中进行线性范围查询,但嵌套并不总是一个坏主意。让我举一个可用于日常统计的文档示例:

{
    hours: [
        {views: 2, unique: 1} // This is actually index 0 which denotes hour 0 of the day
    ],
    pageviews: 1000,
    unique_visitors: 4,
    visitors: 67,
    clicks: 5
}

为了便于查询,您将看到如何将一天中的小时数放入子文档中。这意味着要查询当天的统计信息,我只需要进行一次往返,但是我没有失去真正的分析能力,因为我不太可能在复杂查询中使用两天的小时子文档。

所以,我个人会注意到我的评论,并尝试对你的数据进行一些非规范化处理。您认为使用MongoDB atm会过于规范化。

答案 2 :(得分:0)

您确定在用户级别根据所有内容汇总跟踪数据真的值得吗?如何处理这样的时间戳:

{
 userId: 1234,
 registered: ISODate(""),
 visits: [ 
   ISODate(""), 
   ISODate(""),
   ISODate("")
 ],
 clicks: [
   ISODate(""),
   ISODate("")
 ]
}

然后只是通过注册日期匹配的聚合框架,例如计算访问次数。

如果您能够对用户集合进行额外查找,则最好以对象为基础存储跟踪数据:

visits_collection
{
  {userId: 1234, time: ISODate(""), registration: ISODate("")},
  {userId: 1234, time: ISODate(""), registration: ISODate("")},
  {userId: 1234, time: ISODate(""), registration: ISODate("")},
}

要再次查询,请使用聚合框架。如果您愿意,这也可以是上限集合并在注册字段上有索引。它也更灵活,因为您可以添加更多字段,例如稍后访问持续时间。