2014-06-26 122 views
8

假設我們有一個社交網絡應用(使用NodeJS,Express)和MongoDB作爲主數據庫引擎。MongoDB和Redis作爲緩存層架構

在大多數來自客戶端(移動應用程序,Web應用程序等)的API調用中,我不想爲每個請求進行復雜的查詢。例如,這些請求可以從緩存層回覆,例如Redis。

但我的問題是如何/何時更新緩存層,因爲所有的寫操作都在MongoDB數據庫中執行,而不是緩存層(Redis)。解決這個問題的正確方法/體系結構是什麼?

回答

15

這真的取決於你的需要,但這裏有一個相當普遍的一種:

on_get_request 
    if data_in_redis 
    serve_data_from _redis 
    else 
    get_data_from_mongo 
    set_data_in_redis 
    set_expire_in_redis 
    serve_data_from_memory 

數據將在時間有點陳舊,但沒關係大多數用例。它的工作原理以及在一些緩存失效組合時的重要數據被寫入:

on_important_data 
    delete_invalid_redis_keys 

但是,這一切都假定低寫入,高讀取和穩定組查詢。

你的高負載用例是什麼樣的?

+0

cron作業如何填充緩存? –

+0

如果a)可接受的陳舊時間較長且b)讀取時間較長,則可以發揮作用。這種情況並不少見,一個好的例子是mongo map reduce,其中10分鐘的結果足夠好,mongo map每10分鐘就會減少一次redis更新。 – generalhenry

5

Idel方法是寫回緩存方式。 你可以先寫mongodb然後寫入redis。這是最常見的方式。

另一種選擇是, 你可以先寫redis並使用redis發送異步消息(如Q) 某些線程可以使用消息並讀取它,將其寫入mongoDB。

第一個選項更容易實現。 第二個選項可以支持大量的寫入事務。 據我所知,mongodb鎖定問題尚未解決(它已從全局鎖定到db級別鎖定) 第二個選項對於減少這種鎖定爭用是相當大的。

+0

實際測試表明,鎖是不是因爲通常情況下,磁盤IO最終會被最大化。只要模式和索引完成最佳,每個插入的延遲可以隨着DB /索引的大小增長而保持不變。 –

0

這將需要一些嚴重的數據抽取,以使Redis成爲MongoDB緩存層的可行選項,同時銘記MongoDB本身有一個工作集保存在RAM中;因爲如果你知道自己在做什麼並且正確計劃你的模式,這兩者都可以從內存中實際服務。

通常轉向Redis的緩存是巨大的網站如Craigslist網站(http://www.slideshare.net/jzawodn/living-with-sql-and-nosql-at-craigslist-a-pragmatic-approach),誰,因爲你可以看到在該演示文稿的幻燈片7使用它的目標是:

  • 櫃檯
  • 斑點
  • 隊列

多,但你可以很容易地看到自己的memcached如何安裝也可以與它合併,包括某些帖子,以及如果MongoDB的是他們的主要存儲而不是MySQL。

因此,演示本身可以讓您瞭解其他人如何在MongoDB中使用Redis。

基本上它通常用來保存數據的快照,通常來自數據庫的速度太慢。

這裏有一些相關的信息,我將用它來解答我的答案:What is Redis and what do I use it for?。我強烈建議你閱讀這個問題,因爲它會讓你更清楚地知道Redis的用途以及它可以做什麼。

+0

不要忘記,MongoDB既可以複製數據,也可以將它作爲日誌和數據文件清理到磁盤,這會相對於純內存解決方案而言變慢。 MongoDB *可以配置爲僅在內存中 - 請參閱我的關於「Socialite」項目如何實現的答案。 –

0

您是否需要交易和實時寫入?當有人在mongo上寫入更新時,是否需要立即通知客戶更改(1秒/分鐘/天)?

您的數據真的很重要,任何寫入都不應該丟失?如果是的話,除了使用AOF(這不是redis上的默認模式並且速度更慢)之外,您不能先在redis上寫入數據。例如,mongo和redis之間的交易不會那麼容易實現。

如果您先在redis中編寫代碼,您可以使用發佈/訂閱來通知訂閱了更新mongo中的值的redis客戶端,但不能保證您的數據安全地傳輸,並且要警告!但是,這應該是更新所有連接到redis的客戶端的最快/最高性能的方式。

另一種方法是,你可以定義一個輪詢,使用redis和mongo之間的實時可接受間隔來更新緩存,並且不需要從代碼直接寫入redis,而是從mongo更改爲redis(去耦)。您可以使用監聽器(mongo中的「觸發器」)來執行此操作,或使用髒檢查。

最後,有些已經從mongo + redis遷移到像viber這樣的couchbase,也許你應該考慮它是一個選項? http://www.couchbase.com/viber

+0

我只讀了幾行,但MongoDB沒有觸發器 – Sammaye

5

這已經在MongoDB open source project called "Socialite"的參考架構中實現,雖然它是Java而不是node.js,所以我的答案是基於我的經驗壓力和負載測試代碼。

正如您可以從狀態源的實施中看到的那樣,the feed has option fanoutOnWrite cache對於活動用戶將爲create a cache (limited size document),其上限爲高速緩存文檔中最新條目的數量(該數字是可配置的)。

該實現的關鍵原則是內容需求實際上與時間線緩存需求不同,寫入內容數據庫首先是所有內容的記錄系統,然後更新緩存(如果它存在)。如果需要,這部分can be done asynchronously。該更新使用「加蓋陣列」(又名update $slice functionality)以原子方式將新值/內容推送到陣列,並同時關閉最舊的一個。

如果用戶沒有存在,請不要創建緩存(如果他們從未登錄,那麼您正在浪費精力)。或者,您可以基於某個TTL參數使緩存過期。

當您在用戶登錄時爲其讀取緩存並且它不在那裏時,然後回到「fanoutOnRead」(查詢他們所關注的用戶的所有內容),然後從該結果中構建緩存。

Socialite項目在所有後端都使用了MongoDB,但是在對它進行基準測試時,我們發現時間線緩存不需要被複制或保存,因此它的MongoDB服務器被配置爲僅在「內存」(沒有日誌,不是複製,無磁盤刷新),這與您的Redis使用類似。如果你失去了緩存,它將只是從永久內容數據庫「按需」重建。

+0

不錯,我的投票 – Sammaye

1

由於您的問題是關於架構,並以「假設...「

任何理由選擇MongoDB的?

在Postgres我得到比MongoDB的更好的性能和最佳的與Postgres的JSON/jsonb支持關係型和無結構文件實際上快於MongoDB是隨着Postgres的你會得到一個可靠的它具有優異的性能,可擴展性和最重要的戰鬥硬化的數據庫可以讓你在晚上睡覺,享受你的假期。

您還可以使用Postgres的LISTEN/NOTIFY實時事件,以便您可以執行Redis的緩存無效。

這是一個例子o F使用Postgres的LISTEN/NOTIFY中的NodeJS: http://gonzalo123.com/2011/05/23/real-time-notifications-part-ii-now-with-node-js-and-socket-io/

下面是Postgres的9.4一些綜合性的業績基準爲無模式/ NOSQL文檔存儲VS的MongoDB:

http://thebuild.com/presentations/pg-as-nosql-pgday-fosdem-2013.pdf

+0

你有我的注意力「讓你晚上睡覺,享受你的假期」:-)。感謝您的鏈接。 –