2012-12-04 46 views
2

我很好奇是否有任何最佳实践将按月/日汇总的集合的指标编入索引。按月或日汇总的指标的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: ... 
     }, 
     ... 
    } 
} 
+0

最佳索引策略取决于您最频繁和最具性能关键的查询。 – Philipp

+0

我会查询大部分指标。为每个可能的date.month创建一个索引似乎很疯狂,并且每次创建新月时都必须更新该索引。如果你在所有的“轨道”上放置一个指数,那么所有的指标都会得到指数收益? – CrashRoX

+0

按照您的想法更新索引不是一种合理的方式,也就是说索引甚至可以工作。嗯听起来像你需要重新思考你的模式,并不是所有的都在这里,但我会说你对你的场景过于规范化 – Sammaye

回答

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, ... } 
    ] 
+0

你是否建议使用时间戳而不是年和月?这对索引和空间使用有什么影响吗? – CrashRoX

+0

时间戳BSON数据类型仅供MongoDB内部使用。用户应该使用日期(也包括时间)或他们自己的时间戳约定。考虑到MongoDB不会压缩字段名称(当您有1000个具有“month”字段的对象时,它至少会存储1000个字符串“month”的实例),将全部日期信息放入一个领域。 – Philipp

+0

关于单字段索引与复合字段索引的性能:我认为没有太大区别(只要你使用完整的索引),但是当有一个索引时,场指数最有可能表现更好。 – Philipp

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过于规范。

+0

这实际上是我原本想要走下去的道路。问题是还有一些我想查询的其他数据。该文件是给用户的。随着时间的推移跟踪他们的浏览量,访问量等。用户在文档上有其他数据。例如,这是一个registeration_date。目标是能够说“向我显示在X日期和Y日期之间注册并且在A日期和B日期之间具有更多Z页面浏览量的用户”。我一直没有能够想出一个没有嵌入的架构来促进这一点。 – CrashRoX

+0

@CrashRoX在这种情况下,我可能会将注册日期复制到统计信息中,或者您可以获取满足条件的user_id列表,然后搜索统计信息 – Sammaye

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("")}, 
} 

再次查询使用聚合框架。这也可以是一个封顶的集合,并在注册字段上有一个索引,如果你喜欢。它也更灵活,因为您可以添加更多字段,例如稍后访问持续时间。