2011-04-09 24 views
3

我正在嘗試使用GAE數據存儲進行一些練習,以獲得關於查詢和帳單機制的感受。GAE數據存儲 - 當寫入數量多於讀取數量時的最佳實踐

我已閱讀了關於GAE的Oreilly書,並觀看了關於數據存儲區的Google視頻。我的問題是,最佳實踐方法通常涉及更多的讀取操作,而不是寫入數據存儲區。

我建了一個超級簡單的應用程序:

  • 有兩個網頁 - 一個 選擇鏈接,以及一個視圖選擇 鏈接
  • 每個用戶都可以選擇URL鏈接添加到自己的「鏈接供稿「
  • 用戶可以隨時選擇他想要的任意數量的鏈接。
  • 在不同的網頁上,我想向用戶展示他選擇的最近10個鏈接。
  • 每個用戶都有自己的「鏈接Feed」網頁。
  • 每個「鏈接」我想保存並顯示一些元數據 - 例如:url鏈接本身;當它被選中時;飼料已經出現了多少次;等

在這種情況下,因爲用戶可以選擇他想要儘可能多的聯繫,只要他想,我的應用程序寫入到數據存儲,比數量更讀(寫 - 當用戶選擇了另一個鏈接;讀 - 用戶打開網頁時,看到他的「鏈接飼料」)

問題1: 我能想到的(至少)兩個選項如何處理數據爲這個應用程序:

選項A: - 與用戶維護每個用戶的實體詳細信息,登記等 - 每個用戶保存他的近10間選擇的聯繫,他問之後,將呈現給用戶的網頁維護另一個實體爲它

選項B: - 維護每個URL鏈接實體 - 這意味着所有用戶的所有網址將被存儲爲相同的對象 - 保持實體每個用戶的詳細信息(與選項A相同),但在網址的大表中添加對用戶網址的引用

什麼會是更好的方法嗎?

問題2: 如果我要算的網址總數選擇,直到今天,還是用戶選擇的URL的每日量,或其他任何計數 - 我應該用它與我的SDK工具,還是應該我在上述實體中插入計數器? (我想,以減少數據存儲量爲寫盡我所能)

編輯(應答@埃拉德的評論): 假設我想只保存每用戶10頁最後的URL。其他人我想擺脫(所以不要過度填充我的數據庫與不必要的數據)。

編輯2:添加代碼之後 所以我做了下面的代碼在try(嘗試第一埃拉德的方法):

這裏是我的類:

class UserChannel(db.Model): 
currentUser = db.UserProperty() 
userCount = db.IntegerProperty(default=0) 
currentList = db.StringListProperty() #holds the last 20-30 urls 

然後我序列化的網址&將元數據轉換爲JSON字符串,用戶從第一頁開始POST。 這裏的POST被如何處理:

def post(self): 
    user = users.get_current_user() 
    if user: 
     logging messages for debugging 
     self.response.headers['Content-Type'] = 'text/html' 
     #self.response.out.write('<p>the user_id is: %s</p>' % user.user_id())    
     updating the new item that user adds 
     current_user = UserChannel.get_by_key_name(user.nickname()) 
     dataJson = self.request.get('dataJson') 
     #self.response.out.write('<p>the dataJson is: %s</p>' % dataJson) 
     current_user.currentPlaylist.append(dataJson) 
     sizePlaylist= len(current_user.currentPlaylist) 
     self.response.out.write('<p>size of currentplaylist is: %s</p>' % sizePlaylist) 
     #whenever the list gets to 30 I cut it to be 20 long 
     if sizePlaylist > 30: 
      for i in range (0,9): 
       current_user.currentPlaylist.pop(i) 
     current_user.userCount +=1 
     current_user.put() 
     Updater().send_update(dataJson) 
    else: 
     self.response.headers['Content-Type'] = 'text/html' 
     self.response.out.write('user_not_logged_in') 

,其中更新是我與通道API更新網頁與飼料法。我可以看到每個用戶都有一個帶有20-30個鏈接的ListProperty(當它打到30個時,我用pop()將它縮小到了20個),但!!!!!!!!!!!!!!!。。。。。。。。。。。。。。。。。。。。。。價格相當高... 每個郵政像這裏一個需要〜200ms,121 cpu_ms,cpm_usd = 0.003588。這是非常昂貴的考慮到我所做的就是將一個字符串保存到列表中... 我認爲問題可能是實體與大ListProperty變大了嗎?

+0

從您的問題中不清楚您是要保存用戶選擇的所有網址,還是隻保存最近的20個網址,並丟棄較舊的網址。你能澄清嗎? – Elad 2011-04-09 08:14:36

+0

Elad你是對的。這是兩種不同的情況。我用你評論的內容更新了我的問題。 – 2011-04-09 08:33:13

+0

太棒了 - 這使得它更簡單 - 見下面我的答案。順便說一句,出於純粹的好奇心,應用程序是什麼?爲什麼我想要保存一些URL,但只看到最後10個? – Elad 2011-04-09 12:57:15

回答

1

首先,您有權擔心大量寫入GAE數據存儲 - 我自己的經驗是,與讀取相比,它們非常昂貴。例如,我的一個應用程序除了在單個模型表中插入記錄外,沒有做任何事情,每天都有幾十個數千次的寫入,因此耗盡了免費配額。因此,高效地處理寫入直接轉化爲您的底線。

首先問

我不會存儲鏈接作爲獨立的實體。數據存儲區不是RDBMS,因此標準規範化實踐不一定適用。對於每個用戶實體,使用ListProperty存儲最新的URL及其元數據(可以將所有內容序列化爲字符串)。

  • 由於您只更新單個記錄,因此只需用戶添加鏈接,就不會更新所有鏈接記錄,因此可以高效編寫。請記住,要保留一個滾動列表(FIFO)並將引用URL存儲爲單獨的模型,每個新的URL都意味着兩個寫入操作 - 插入新的URL,刪除最舊的URL。
  • 這對閱讀也很有效,因爲用戶記錄上的一次讀取爲您提供了呈現用戶提要所需的所有數據。
  • 從存儲的角度來看,世界上的URL總數遠遠超過了您的用戶數量(即使您成爲下一個Facebook),用戶選擇的URL的差異也是如此,所以很可能平均值URL只有一個用戶 - 在RDBMS風格的數據標準化方面沒有真正的收益。

另一個優化的想法:如果您的用戶通常在短時間內添加幾個鏈接,您可以嘗試批量寫入,而不是單獨寫入。使用memcache存儲新添加的用戶URL,並使用任務隊列定期將該瞬態數據寫入持久數據存儲。我不確定使用任務的資源成本是多少 - 您必須檢查。 Here's a good article閱讀這個問題。

第二個問題

使用計數器。請記住,它們在分佈式環境中不是微不足道的,因此請仔細閱讀 - 有關此主題的許多GAE文章,食譜和博客文章 - 只需google appengine counters。在這裏,使用memcache也應該是一個很好的選擇,以減少數據存儲區的寫入次數。

+0

謝謝@Elas!那麼,當你說「將所有東西串行化爲一個字符串」時,你的意思是把它做成像 - 例如 - 在URL中有許多參數的youtube url?很多=&和?區分不同的參數? – 2011-04-10 05:35:55

+0

將所有內容序列化爲一個字符串可以避免2次寫入,但請記住,如果您想在將來例如將任何屬性與鏈接一起存儲共享它的用戶數量,那麼你將無法搜索和排序你的屬性,你將不得不反序列化列表屬性元素來查找屬性的值。 – Yasser 2011-04-10 06:11:52

+0

@Hamutsi問題是ListProperty不能存儲任意對象,所以如果你想存儲除了URL之外的信息,你需要以某種方式序列化它。 URL參數是一個有趣的想法。我正在考慮更多的JSON。 – Elad 2011-04-10 06:52:58

1

回答1

商店鏈接作爲獨立的實體。還爲每個用戶存儲一個實體,其中ListProperty具有最近20個鏈接的鍵。當用戶選擇更多鏈接時,您只需更新密鑰的ListProperty。 ListProperty維護訂單,因此只要遵循FIFO插入順序,您不必擔心所選鏈接的時間順序。

當您想要顯示用戶選擇的鏈接(第2頁)時,您可以通過一鍵獲取(鍵)來獲取一次通話中的所有用戶鏈接。

答案2

肯定會保留櫃檯,作爲實體的數量增長,計數記錄的複雜性將繼續增加,但設有專櫃,業績將保持不變。

+0

謝謝亞瑟。我在考慮使用引用鍵的ListProperty,但這是我的恐懼 - 現在對於每一個鏈接選擇,都有兩個寫入數據存儲區的操作:1-添加新的鏈接實體; 2 - 更新用戶實體中最新更新鏈接的列表。假設用戶繼續選擇鏈接,是不是很昂貴? – 2011-04-09 08:27:26

+1

如果您爲每個鏈接創建一個實體並將用戶保留爲其中的參考屬性,那麼查找用戶最近20個鏈接的成本將隨着用戶和鏈接數量的增加而持續增加。您可以通過鏈接實體中的附加時間戳降低該成本,並在用戶和時間戳上具有複合索引以加快查找速度。 – Yasser 2011-04-09 08:46:03

+0

Yasser,當您寫下「如果您爲每個鏈接創建一個實體並將用戶保留爲參考屬性」,您的意思不是您之前建議的內容,或者另外(對ListProperty中的參考鍵) – 2011-04-09 10:01:43