1. 程式人生 > >Memcache應用場景介紹

Memcache應用場景介紹

我會 track post sel ttl 應用場景 內存使用率 集成 影響

面臨的問題

對於高並發高訪問的Web應用程序來說,數據庫存取瓶頸一直是個令人頭疼的問題。特別當你的程序架構還是建立在單數據庫模式,而一個數據池連接數峰 值已經達到500的時候,那你的程序執行離崩潰的邊緣也不遠了。非常多小站點的開發者一開始都將註意力放在了產品需求設計上,缺忽視了程序總體性能,可擴 展性等方面的考慮,結果眼看著訪問量一天天網上爬,可突然發現有一天站點由於訪問量過大而崩潰了。到時候哭都來不及。所以我們一定要未雨綢繆。在數據庫還 沒罷工前。想方設法給它減負。這也是這篇文章的主要議題。

大家都知道,當有一個request過來後。webserver交給appserver,app處理並從db中存取相關數據,但db存取的花費是相當高昂的。特 別是每次都取同樣的數據,等於是讓數據庫每次都在做高耗費的無用功。數據庫假設會說話。肯定會發牢騷,你都問了這麽多遍了,難道還記不住嗎?是啊,假設 app拿到第一次數據並存到內存裏,下次讀取時直接從內存裏讀取,而不用麻煩數據庫,這樣不就給數據庫減負了?並且從內存取數據必定要比從數據庫媒介取快 非常多倍,反而提升了應用程序的性能。

因此,我們能夠在web/app層與db層之間加一層cache層,主要目的:1. 降低數據庫讀取負擔;2. 提高數據讀取速度。並且,cache存取的媒介是內存。而一臺server的內存容量一般都是有限制的,不像硬盤容量能夠做到TB級別。

所以。能夠考慮採用分布 式的cache層。這樣更易於破除內存容量的限制。同一時候又添加了靈活性。

Memcached 介紹

Memcached是開源的分布式cache系統,如今非常多的大型web應用程序包含 facebook,youtube,wikipedia。yahoo等等都在使用memcached來支持他們每天數億級的頁面訪問。

通過把cache層 與他們的web架構集成,他們的應用程序在提高了性能的同一時候,還大大減少了數據庫的負載。

詳細的memcached資料大家能夠直接從它的官方站點[1]上得到。這裏我就簡單給大家介紹一下memcached的工作原理:

Memcached處理的原子是每個(key,value)對(下面簡稱kv對)。key會通過一個hash算法轉化成hash-key,便於查找、對照以及做到盡可能的散列。同一時候,memcached用的是一個二級散列。通過一張大hash表來維護。

Memcached有兩個核心組件組成:服務端(ms)和client(mc),在一個memcached的查詢中,mc先通過計算key的hash值來 確定kv對所處在的ms位置。

當ms確定後,client就會發送一個查詢請求給相應的ms,讓它來查找確切的數據。

由於這之間沒有交互以及多播協議,所以 memcached交互帶給網絡的影響是最小化的。

舉例說明:考慮下面這個場景,有三個mc各自是X,Y,Z,還有三個ms各自是A,B,C:

設置kv對 X想設置key=”foo”,value=”seattle” X拿到ms列表,並對key做hash轉化,依據hash值確定kv對所存的ms位置 B被選中了 X連接上B,B收到請求,把(key=”foo”,value=”seattle”)存了起來

獲取kv對 Z想得到key=”foo”的value Z用同樣的hash算法算出hash值,並確定key=”foo”的值存在B上 Z連接上B,並從B那邊得到value=”seattle” 其它不論什麽從X。Y,Z的想得到key=”foo”的值的請求都會發向B

技術分享

Memcachedserver(ms)

內存分配

默認情況下,ms是用一個內置的叫“塊分配器”的組件來分配內存的。舍棄c++標準的malloc/free的內存分配。而採用塊分配器的主要目的 是為了避免內存碎片,否則操作系統要花費很多其它時間來查找這些邏輯上連續的內存塊(實際上是斷開的)。用了塊分配器,ms會輪流的對內存進行大塊的分配,並 不斷重用。當然因為塊的大小各不同樣。當數據大小和塊大小不太相符的情況下。還是有可能導致內存的浪費。

同一時候,ms對key和data都有對應的限制,key的長度不能超過250字節,data也不能超過塊大小的限制 --- 1MB。 由於mc所使用的hash算法,並不會考慮到每一個ms的內存大小。理論上mc會分配概率上等量的kv對給每一個ms,這樣假設每一個ms的內存都不太一樣。那 可能會導致內存使用率的減少。所以一種替代的解決方式是。依據每一個ms的內存大小。找出他們的最大公約數,然後在每一個ms上開n個容量=最大公約數的 instance,這樣就等於擁有了多個容量大小一樣的子ms。從而提供總體的內存使用率。

緩存策略

當ms的hash表滿了之後,新的插入數據會替代老的數據。更新的策略是LRU(近期最少使用),以及每一個kv對的有效時限。Kv對存儲有效時限是在mc端由app設置並作為參數傳給ms的。

同一時候ms採用是偷懶替代法。ms不會開額外的進程來實時監測過時的kv對並刪除。而是當且僅當,新來一個插入的數據,而此時又沒有多余的空間放了。才會進行清除動作。

緩存數據庫查詢 如今memcached最流行的一種使用方式是緩存數據庫查詢,以下舉一個簡單樣例說明:

App須要得到userid=xxx的用戶信息,相應的查詢語句類似:

“SELECT * FROM users WHERE userid = xxx”

App先去問cache,有沒有“user:userid”(key定義可預先定義約束好)的數據,假設有,返回數據;假設沒有,App會從數據庫中讀取數據。並調用cache的add函數,把數據增加cache中。

當取的數據須要更新,app會調用cache的update函數,來保持數據庫與cache的數據同步。

從上面的樣例我們也能夠發現,一旦數據庫的數據發現變化,我們一定要及時更新cache中的數據,來保證app讀到的是同步的正確數據。當然我們可 以通過定時器方式記錄下cache中數據的失效時間,時間一過就會激發事件對cache進行更新,但這之間總會有時間上的延遲。導致app可能從 cache讀到臟數據,這也被稱為狗洞問題。(以後我會專門描寫敘述研究這個問題)

數據冗余與故障預防

從設計角度上,memcached是沒有數據冗余環節的,它本身就是一個大規模的高性能cache層,增加數據冗余所能帶來的僅僅有設計的復雜性和提高系統的開支。

當一個ms上丟失了數據之後。app還是能夠從數據庫中取得數據。

只是更慎重的做法是在某些ms不能正常工作時,提供額外的ms來支持cache。這樣就不會由於app從cache中取不到數據而一下子給數據庫帶來過大的負載。

同一時候為了降低某臺ms故障所帶來的影響,能夠使用“熱備份”方案,就是用一臺新的ms來代替有問題的ms,當然新的ms還是要用原來ms的IP地址,大不了數據又一次裝載一遍。

第二種方式。就是提高你ms的節點數,然後mc會實時偵查每一個節點的狀態。假設發現某個節點長時間沒有響應,就會從mc的可用server列表裏 刪除,並對server節點進行又一次hash定位。當然這樣也會造成的問題是,原本key存儲在B上,變成存儲在C上了。所以此方案本身也有其弱點,最好 能和“熱備份”方案結合使用。就能夠使故障造成的影響最小化。

Memcachedclient(mc)

Memcachedclient有各種語言的版本號供大家使用。包含java。c。php,.net等等。詳細可參見memcached api page[2]。 大家能夠依據自己項目的須要,選擇合適的client來集成。

緩存式的Web應用程序架構 有了緩存的支持。我們能夠在傳統的app層和db層之間增加cache層,每一個appserver都能夠綁定一個mc。每次數據的讀取都能夠從ms中取得,假設 沒有,再從db層讀取。而當數據要進行更新時。除了要發送update的sql給db層。同一時候也要將更新的數據發給mc。讓mc去更新ms中的數據。

技術分享

如果今後我們的數據庫能夠和ms進行通訊了,那能夠將更新的任務統一交給db層。每次數據庫更新數據的同一時候會自己主動去更新ms中的數據,這樣就能夠進一步降低app層的邏輯復雜度。例如以下圖:

技術分享

只是每次我們假設沒有從cache讀到數據。都不得不麻煩數據庫。

為了最小化數據庫的負載壓力,我們能夠部署數據庫復寫,用slave數據庫來完畢讀取操作,而master數據庫永遠僅僅負責三件事:1.更新數據;2.同步slave數據庫;3.更新cache。例如以下圖:

技術分享

以上這些緩存式web架構在實際應用中被證明是能有效並能極大地減少數據庫的負載同一時候又能提高web的執行性能。

當然這些架構還能夠依據詳細的應用環境進行變種,以達到不同硬件條件下性能的最優化。

Memcache應用場景介紹