2012-12-04 45 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("")}, 
} 

再次查詢使用聚合框架。這也可以是一個封頂的集合,並在註冊字段上有一個索引,如果你喜歡。它也更靈活,因爲您可以添加更多字段,例如稍後訪問持續時間。