2009-10-22 79 views
19

我們試圖在寫入數據庫時​​更新memcached對象,以避免在插入/更新後必須從數據庫讀取它們。Memcached,鎖定和競爭條件

對於我們的論壇帖子對象,我們有一個包含查看帖子次數的ViewCount字段。

我們擔心我們正在通過更新memcached對象來引入競爭條件,因爲可以在服務器場的另一臺服務器上同時查看同一帖子。

任何想法如何處理這些類型的問題 - 似乎需要某種鎖定,但如何在服務器場中的服務器之間可靠地執行此操作?

回答

15

如果您處理的數據不一定是需要需要實時更新,而對於我來說,查看計數就是其中之一,那麼您可以將expires字段添加到存儲在memcache中的對象。

一旦到期發生,它將返回到數據庫並讀取新的值,但在此之前它將保持獨立。

當然,對於您可能希望更新頻率更新的新帖子,您可以對此進行編碼。

Memcache只在其實例中存儲了一個對象的副本,而不是其中的很多副本,所以我不擔心對象鎖定或任何事情。這是爲了處理數據庫,而不是緩存。

編輯:

Memcache中並沒有保證,當你得到,並從您的數據將不會打一頓豐盛的服務器上設置這一點。

從內存緩存文檔:

  • 的一系列命令是不是原子。如果您針對某個項目發出「獲取」,請對數據進行操作,然後希望將其「設置」回memcached中,但不能保證它是唯一處理該值的進程。同時,您最終可能會覆蓋由其他內容設置的值。

比賽條件和陳舊的數據

有一點要記住,你設計你的應用程序緩存數據,是如何處理競爭條件和偶爾的陳舊數據。

假設您緩存最新的五條評論,以便在應用程序的側邊欄上顯示。您決定只需每分鐘刷新一次數據。但是,您忽略了記住此邊欄顯示每秒渲染50次!因此,一旦60秒週轉並且高速緩存到期,突然有10多個進程運行相同的SQL查詢來重新填充該高速緩存。每次緩存過期時,都會導致突發的SQL通信量突然增加。

更糟糕的是,您有多個進程更新相同的數據,而錯誤的進程約會緩存。那麼你有過時的,過時的數據浮動。

一個人應該留意填充或重新填充緩存時可能出現的問題。請記住,檢查memcached,獲取SQL以及存儲到memcached中的過程根本不是原子!

+0

問題是,我們希望視圖計數(在這種情況下,但有其他情況下有同樣的問題)需要實時更新 - 您單擊帖子,視圖計數增加。我們也希望緩存對象儘可能長時間生活,當然這是出於性能方面的考慮。 – Micael 2009-10-22 18:29:20

+1

你不能保證你想要做的事情。 memcache爲您提供的是可伸縮性,而非原始性能。 – Nathan 2009-10-22 18:43:42

1

memcached操作是原子操作。服務器進程將排隊請求並在進入下一個請求之前完全服務每個請求,因此不需要鎖定。

編輯: memcached有一個增量命令,其中原子。您只需將計數器作爲單獨的值存儲在緩存中即可。

+6

真的,但我這種情況下,我們將獲得該項目,增加ViewCount和再次將它,並作爲內森也說,這不是一個原子操作 – Micael 2009-10-23 06:52:47

3

我在想 - 解決方案可以單獨存儲Postcount從Post對象,然後做一個INCR。當然這需要在顯示信息時從memcached讀取2個獨立的值。

1

我們在我們的系統中遇到過這個問題。我們改性得到這麼

  • 如果該值未設置,它與一個標記(「G」)和[8]第二TTL設置它,並返回false因此調用函數生成它。
  • 如果該值未被標記(!=='g'),則反序列化並返回它。
  • 如果該值被標記(==='g'),然後等待1秒鐘,然後重試,直到它沒有被標記。它最終將由另一個進程設置,或由TTL過期。

當我們實現這個時,我們的數據庫負載下降了100倍。

function get($key) { 
    $value=$m->get($key); 
    if ($value===false) $m->set($key, 'g', $ttl=8); 
    else while ($value==='g') { 
    sleep(1); 
    $value=$m->get($key); 
    } 
    return $value; 
} 
+0

更好地使用cas()插入set() http:// php達網絡/手動/ RU/memcached.cas.php – Hett 2015-08-20 12:03:30