利用memcached構建高性能的Web應用程序

面臨的問題php

對於高併發高訪問的Web應用程序來講,數據庫存取瓶頸一直是個使人頭疼的問題。特別當你的程序架構仍是創建在單數據庫模式,而一個數據池鏈接數峯值已經達到500的時候,那你的程序運行離崩潰的邊緣也不遠了。不少小網站的開發人員一開始都將注意力放在了產品需求設計上,缺忽視了程序總體性能,可擴展性等方面的考慮,結果眼看着訪問量一每天網上爬,可忽然發現有一天網站由於訪問量過大而崩潰了,到時候哭都來不及。因此咱們必定要未雨綢繆,在數據庫還沒罷工前,千方百計給它減負,這也是這篇文章的主要議題。java

你們都知道,當有一個request過來後,web服務器交給app服務器,app處理並從db中存取相關數據,但db存取的花費是至關高昂的。特別是每次都取相同的數據,等因而讓數據庫每次都在作高耗費的無用功,數據庫若是會說話,確定會發牢騷,你都問了這麼多遍了,難道還記不住嗎?是啊,若是app拿到第一次數據並存到內存裏,下次讀取時直接從內存裏讀取,而不用麻煩數據庫,這樣不就給數據庫減負了?並且從內存取數據必然要比從數據庫媒介取快不少倍,反而提高了應用程序的性能。c++

所以,咱們能夠在web/app層與db層之間加一層cache層,主要目的:1. 減小數據庫讀取負擔;2. 提升數據讀取速度。並且,cache存取的媒介是內存,而一臺服務器的內存容量通常都是有限制的,不像硬盤容量能夠作到TB級別。因此,能夠考慮採用分佈式的cache層,這樣更易於破除內存容量的限制,同時又增長了靈活性。web

Memcached 介紹算法

Memcached是開源的分佈式cache系統,如今不少的大型web應用程序包括facebook,youtube,wikipedia,yahoo等等都在使用memcached來支持他們天天數億級的頁面訪問。經過把cache層與他們的web架構集成,他們的應用程序在提升了性能的同時,還大大下降了數據庫的負載。
具體的memcached資料你們能夠直接從它的官方網站[1]上獲得。這裏我就簡單給你們介紹一下memcached的工做原理:sql

Memcached處理的原子是每個(key,value)對(如下簡稱kv對),key會經過一個hash算法轉化成hash-key,便於查找、對比以及作到儘量的散列。同時,memcached用的是一個二級散列,經過一張大hash表來維護。數據庫

Memcached有兩個核心組件組成:服務端(ms)和客戶端(mc),在一個memcached的查詢中,mc先經過計算key的hash值來肯定kv對所處在的ms位置。當ms肯定後,客戶端就會發送一個查詢請求給對應的ms,讓它來查找確切的數據。由於這之間沒有交互以及多播協議,因此memcached交互帶給網絡的影響是最小化的。api

舉例說明:考慮如下這個場景,有三個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

Memcached服務器(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上了。因此此方案自己也有其弱點,最好能和「熱備份」方案結合使用,就可使故障形成的影響最小化。

Memcached客戶端(mc)

Memcached客戶端有各類語言的版本供你們使用,包括java,c,php,.net等等,具體可參見memcached api page[2]。
你們能夠根據本身項目的須要,選擇合適的客戶端來集成。

緩存式的Web應用程序架構
有了緩存的支持,咱們能夠在傳統的app層和db層之間加入cache層,每一個app服務器均可以綁定一個mc,每次數據的讀取均可以從ms中取得,若是沒有,再從db層讀取。而當數據要進行更新時,除了要發送update的sql給db層,同時也要將更新的數據發給mc,讓mc去更新ms中的數據。

以上這些緩存式web架構在實際應用中被證實是能有效並能極大地下降數據庫的負載同時又能提升web的運行性能。固然這些架構還能夠根據具體的應用環境進行變種,以達到不一樣硬件條件下性能的最優化。

相關文章
相關標籤/搜索