2015-11-25 84 views
1

假設我有一個通用網站,允許某人在很短的時間內下載他們的feed。用戶可以是subscribed與許多不同的pages,並且用戶的feed必須從服務器返回給用戶,而在訂閱的所有頁面之間最近的是最近的posts。最初,當用戶查詢的服務器爲feed,算法如下:Feed算法+數據庫:太多的行或太慢的檢索

  1. 看所有的pages用戶subscribed
  2. 獲得N最近posts從每個page
  3. 排序的所有的的posts
  4. 返回N最近的帖子給用戶作爲他們的feed

事實證明,每次用戶嘗試刷新一個Feed時都很慢。因此,我將數據庫更改爲具有feedposts的表格,該表格僅具有用戶的外鍵和該帖子的外鍵。每次頁面發佈新帖子時,都會爲其每個訂閱的關注者創建一個Feed帖子。這樣,當用戶需要他們的供稿時,它已經被創建,並且不需要在檢索時創建。

我這樣做的方式是創建太多的行,並且看起來不可擴展。例如,如果單個頁面發佈1個帖子,&擁有1,000,000個關注者,我們只在我們的feedpost表中創建了1,000,000個新行。

請幫忙! Facebook等公司如何處理這個問題?他們是否根據要求生成飼料?我的數據庫關係很糟糕嗎?

回答

1

這並不是說原始模式本身會有內在錯誤,至少不是基於您提供的高級描述。緩慢起因於你不能以訪問數據庫的方式訪問數據庫。

通常,在查詢關係數據庫時,應儘可能使用JOIN和數據庫內排序,而不是獲取大量數據,然後嘗試連接相關對象並在代碼中對其進行排序。如果你讓數據庫爲你做所有這些,它會快得多,因爲它可以利用索引,並且只訪問那些實際需要的對象。作爲一個經驗法則,如果您需要在Python代碼中對QuerySet的結果進行排序,或者循環執行多個查詢集並以某種方式將它們組合起來,那麼您很可能會做錯某些事情,並且應該弄清楚如何讓數據庫爲你做。當然,這並不是每一次都是如此,但肯定經常是不夠的。

讓我試着用一段簡單的代碼來說明。假設你有以下型號:

class Page(models.Model): 
    name = models.CharField(max_length=47) 
    followers = models.ManyToManyField('auth.User', related_name='followed_pages') 

class Post(models.Model): 
    title = models.CharField(max_length=147) 
    page = models.ForeignKey(Page, related_name='posts') 
    content = models.TextField() 
    time_published = models.DateTimeField(auto_now_add=True) 

你可以,例如,獲得張貼到網頁其次是當前登錄的用戶使用的代碼下面一行的最後20個職位的名單:

latest_posts = Post.objects.filter(page__followers=request.user).order_by('-time_published')[:20] 

這會針對您的數據庫運行單個SQL查詢,該查詢只返回匹配的(最多)20個結果,而不會返回任何結果。而且,由於您加入了涉及的所有表的主鍵,因此它可以方便地爲所有聯接使用索引,使其非常快速。事實上,這正是關係數據庫被設計爲有效執行的那種操作。

+0

這是驚人的,謝謝。 – Chisx

0

緩存將是這裏的解決方案。 您必須減少數據庫讀取次數,與緩存讀取次數相比要慢得多。

你可以使用類似Redis的東西來緩存帖子。 這裏是爲了更好地理解

Is Redis just a cache

每個頁面可以分配一個鍵一個驚人的答案,你可以把所有的帖子該鍵下該頁面。

您不需要緩存所有內容,只需緩存發送M個帖子,其中M >> N和足夠安全的數據庫調用就足夠了。現在如果用戶請求的帖子超出了M個,那麼它們可以直接從數據庫中提取。

現在,如果您必須生成Feed,則可以進行數據庫調用以獲取所有訂閱的頁面(或者也可以將其放入緩存中),然後從緩存中獲取所需數量的帖子。

這裏的問題是保持緩存最新。

爲此,您可以使用類似django-signals之類的東西。無論何時添加新帖子,都可以使用該信號將其添加到緩存中。

因此,對於每個數據庫寫入,您還必須寫入緩存。 但是,您不必從數據庫讀取數據,因爲Redis是內存數據存儲,與標準關係數據庫相比,它非常快速。

編輯: 這些都是一些更多的文章,可以幫助更好地理解

Does Stack Exchange use caching and if so, how

How Twitter Uses Redis to Scale - 105TB RAM, 39MM QPS, 10,000+ Instances