這次是Fotolog的經(jīng)驗,傳說中比Flickr更大的網(wǎng)站,F(xiàn)otolog在21臺服務(wù)器上部署了51個memcached實例,總計有254G緩存空間可用,緩存了多達(dá)175G的內(nèi)容,這個數(shù)量比很多網(wǎng)站的數(shù)據(jù)庫都要大的多,原文是A Bunch of Great Strategies for Using Memcached and MySQL Better Together,我這里還是選擇性的翻譯以及按照我的理解補(bǔ)充,感謝Todd Hoff,總能給我們一些學(xué)習(xí)的案例,從這里也能看出國外技術(shù)的開放態(tài)度,不似我們,其實就那么點小九九還藏著掖著,好了,進(jìn)入正題。
一、關(guān)于memcached
還不知道這個?那你去面試的時候要吃虧了,趕緊去官方網(wǎng)站看一下http://www.danga.com/memcached/,另外google一下用法,硬盤總是太慢,把數(shù)據(jù)存在內(nèi)存里面吧,如果你只有一臺服務(wù)器,推薦用一下APC(Facebook在用)或者eaccelerator或者Xcache(國人開發(fā)的),這些產(chǎn)品單機(jī)效果更好,如果你需要分布式的緩存方案,那么用memcached吧。
二、memcached如何與mysql并肩作戰(zhàn)?
通過數(shù)據(jù)庫分片來解決數(shù)據(jù)庫寫擴(kuò)展的問題把數(shù)據(jù)庫分片,部署到不同的服務(wù)器上,免得只有一個主服務(wù)器,寫操作成為瓶頸以及可能有的“單點故障”,一般的數(shù)據(jù)庫分片主要是按照業(yè)務(wù)來分,盡可能的拆分業(yè)務(wù),不相干的都獨立起來做成服務(wù)也好
前端mysql和一堆memcached服務(wù)器來應(yīng)付讀的問題應(yīng)用程序首先從memcached中獲取數(shù)據(jù),獲取不到再從數(shù)據(jù)庫中獲得并保存在memcached中,以前看過一篇文章說好的應(yīng)用95%的數(shù)據(jù)從memcache的中獲得,3%的數(shù)據(jù)從mysql的query cache中獲得,剩下2%才去查表,對比一下你的應(yīng)用,差距有多遠(yuǎn)?
通過mysql復(fù)制(master-slave)來解決讀的問題
首先mysql數(shù)據(jù)庫通過master-slave讀寫分離,多個slave來應(yīng)對應(yīng)用程序讀的操作。
三、為什么不用mysql的query cache?
我們都知道m(xù)ysql有個query cache,可以緩存上次查詢的結(jié)果,可實際上幫不上太多的忙,下面是mysql quety cache的不足:
只能有一個實例
意味著你能存儲內(nèi)容的上限就是你服務(wù)器的可用內(nèi)存,一臺服務(wù)器能有多少內(nèi)存?你又能存多少呢?
只要有寫操作,mysql的query cache就失效
只要數(shù)據(jù)庫內(nèi)容稍有改變,那怕改變的是其他行,mysql的query cache也會失效
mysql的query cache只能緩存數(shù)據(jù)庫數(shù)據(jù)行
意味著其他內(nèi)容都不行,比如數(shù)組,比如對象,而memcached理論上可以緩存任何內(nèi)容,甚至文件^_^
四、Fotolog的緩存技術(shù)
非確定性緩存你不確定你要的數(shù)據(jù)緩存中有沒有,你也不知道是不是過期了,于是你就試探性的問memcached,我要的什么什么數(shù)據(jù)你那有嗎?我可不要過期的數(shù)據(jù)啊,memcached告訴你說有并且給你,你就開心了,如果沒有呢,你就要從數(shù)據(jù)庫或者別的地方去獲取了,這是memcached典型的應(yīng)用。主要應(yīng)用在:1.復(fù)雜的數(shù)據(jù)需要多次讀取,你的數(shù)據(jù)庫做了分片處理,從多個數(shù)據(jù)庫中獲取數(shù)據(jù)并組合起來是一個非常大的開銷,你大可以把這些數(shù)據(jù)取出來之后存到memcached中
2.mysql query cache的一個好的替代方案,這樣數(shù)據(jù)庫其他部門改變了,只要自己沒改變就沒問題(注意數(shù)據(jù)庫更新的問題,后面會提到)
3.把關(guān)系或者列表緩存起來,比如某個欄目下的多篇文章列表
4.被多個頁面調(diào)用并且獲取起來很慢的數(shù)據(jù),或者是更新很慢的數(shù)據(jù),比如文章瀏覽排行榜
5.如果cache的開銷超過重新獲取的開銷,那么不要緩存它吧
6.標(biāo)簽云和自動建議(類似google sugest)
例如:當(dāng)一個用戶上傳一個圖片,這個用戶的好友頁面上都要列出這張圖片來,那么把它緩存起來吧。
潛在問題:
memcached消耗的主要是服務(wù)器內(nèi)存,對CPU消耗很小,所以Fotolog把memcached部署在他們的應(yīng)用服務(wù)器上(貌似我們也是這樣),他們遇到了CPU搞到90%的使用率(怎么會那么高?哪出問題了吧)、內(nèi)存回收(這是個大問題)等等問題。
狀態(tài)緩存把應(yīng)用服務(wù)的當(dāng)前狀態(tài)存在memcached中主要應(yīng)用在:
1.“昂貴”的操作,開銷大的操作
2.sessions會話,F(xiàn)lickr把session存在數(shù)據(jù)庫中,個人感覺還是存memcached比較“便宜”些,如果memecached服務(wù)器down掉了,那么重新登錄吧。
3.記錄用戶在線信息(我們也是這樣做的)
確定性緩存對于某些特定數(shù)據(jù)庫的全部內(nèi)容,都緩存到memcached,有一個專門的應(yīng)用服務(wù)來保障你要的數(shù)據(jù)都在memcached中,其他應(yīng)用服務(wù)直接從memcached中獲取數(shù)據(jù)而不去取數(shù)據(jù)庫,因為數(shù)據(jù)庫已經(jīng)全部保存到memcached中并保持同步。主要應(yīng)用在:1.讀取伸展,所有的讀取都從memcached中獲得,數(shù)據(jù)庫沒有負(fù)載 2.”永不過期“(相對的)的數(shù)據(jù),比如行政規(guī)劃數(shù)據(jù),變動很小吧
3.經(jīng)常調(diào)用的內(nèi)容
4.用戶的認(rèn)證信息
5.用戶的概要信息
6.用戶的參數(shù)設(shè)置
7.用戶當(dāng)前常用的媒體文件列表,比如用戶的圖片
8.用戶登錄,不走數(shù)據(jù)庫,只走memcached(個人覺得這個不太好,登錄信息還是需要持久化的,用類似BDB這樣效果也不錯)
使用方式:
1.多個專門的緩存池而不是一個大的緩存服務(wù)器,多個緩存池保障了高可用性,一個緩存實例掛掉了走其他的緩存實例,所有的緩存實例掛掉了,走數(shù)據(jù)庫(估計數(shù)據(jù)庫抗不住^_^)
2.所有的緩存池都用程序來維護(hù),比如數(shù)據(jù)庫有更新時,程序自動把更新后的內(nèi)容同步到多個緩存實例中
3.服務(wù)器重啟之后,緩存要比網(wǎng)站先啟動,這就意味著當(dāng)網(wǎng)站已經(jīng)啟動了,所有的緩存都可用
4.讀取的請求可以負(fù)載均衡到多個緩存實例中去,高性能,高可靠性
潛在的問題:
1.你需要足夠多的內(nèi)存來存儲那么多的數(shù)據(jù)
2.數(shù)據(jù)以行記錄數(shù)據(jù),而memcached以對象來存儲數(shù)據(jù),你的邏輯要把行列的數(shù)據(jù)轉(zhuǎn)換成緩存對象
3.要維護(hù)多個緩存實例非常麻煩,Fotolog用Java/Hibernate,他們自己寫了個客戶端來輪詢
4.管理多個緩存實例會增加應(yīng)用程序的許多開銷,但這些開銷相對于多個緩存得到的好處來說算不了什么
主動緩存數(shù)據(jù)魔法般的出現(xiàn)在緩存中,當(dāng)數(shù)據(jù)庫中有更新的時候,緩存立馬填充,更新的數(shù)據(jù)被調(diào)用的可能性更高(比如一篇新文章,看的的人當(dāng)然多),是非確定性緩存的一種變形(原文是It’s non-deterministic caching with a twist.我覺得這樣翻譯怪怪的)。主要應(yīng)用在:1.預(yù)填充緩存:讓memcached盡可能的少調(diào)用mysql如果內(nèi)容不展現(xiàn)的話。
2.“預(yù)熱”緩存:當(dāng)你需要跨數(shù)據(jù)中心復(fù)制的時候
使用步驟:s
1.解析數(shù)據(jù)庫更新的二進(jìn)制日志,發(fā)現(xiàn)數(shù)據(jù)庫更新時對memcached也進(jìn)行同樣的更新
2.執(zhí)行用戶自定義函數(shù),設(shè)置觸發(fā)器調(diào)用UDF更新,具體參考http://tangent.org/586/Memcached_Functions_for_MySQL.html
3.使用BLACKHOLE策略,傳說中Facebook也用mysql的Blackhole存儲引擎來填充緩存,寫到Blackhole的數(shù)據(jù)復(fù)制到緩存中,F(xiàn)acebook用這來設(shè)置數(shù)據(jù)作廢以及跨國界的復(fù)制,好處是數(shù)據(jù)庫的復(fù)制不走mysql,這就意味著沒有二進(jìn)制日志以及對CPU使用不那么多(啊?難道通過memcached存儲二進(jìn)制日志,然后復(fù)制到不同的數(shù)據(jù)庫?有經(jīng)驗的同志在這個話題上可以補(bǔ)充。)
文件系統(tǒng)緩存把文件直接緩存在memcached中,哇,夠BT的,減輕NFS的負(fù)擔(dān),估計只緩存那些過于熱門的圖片吧。
部分頁面內(nèi)容緩存如果頁面的某些部分獲取起來非常費勁,以其緩存頁面的原始數(shù)據(jù)還不如把頁面的部分內(nèi)容直接緩存起來直接調(diào)用
應(yīng)用程序級別的復(fù)制通過API來更新緩存,API的執(zhí)行細(xì)節(jié)如下:
1.一個應(yīng)用把數(shù)據(jù)寫到某個緩存實例,這個緩存實例把內(nèi)容復(fù)制到其他緩存實例(memcached同步)
2.自動獲得緩存池地址以及實例個數(shù)
3.同時對多個緩存實例更新
4.如果某個緩存實例down掉了,跳到下一個實例,直到更新成功
整個過程非常******以及低開銷
其他技巧1.多節(jié)點以應(yīng)對”單點故障”2.使用熱備技術(shù),當(dāng)某個節(jié)點down掉了,另外一臺服務(wù)自動替換成它的IP,這樣客戶端不用更新memcached的IP地址3.memcached可以通過TCP/UDP訪問,持續(xù)連接可以減輕負(fù)載,系統(tǒng)設(shè)計成可同時承受1000個連接
4.不同的應(yīng)用服務(wù),不同的緩存服務(wù)器群
5.檢查一下你的數(shù)據(jù)大小是否匹配你分配的緩存,更多請參考http://download.tangent.org/talks/Memcached%20Study.pdf
6.不要考慮數(shù)據(jù)行緩存,緩存復(fù)雜的對象
7.不要在你的數(shù)據(jù)庫服務(wù)器上跑memcached,兩個都是吃內(nèi)存的怪獸
8.不要被TCP延遲困擾,本地的TCP/IP對內(nèi)存復(fù)制是做了優(yōu)化的
9.盡可能的并行處理數(shù)據(jù)
10.并不是所有的memcached的客戶端都是一樣的,仔細(xì)研究你用的語言所對應(yīng)的(好像php和memcached配合的不錯)
11.盡可能的是數(shù)據(jù)過期而不是使數(shù)據(jù)無效,memcached可以設(shè)定過期時間
12.選擇一個好的緩存標(biāo)識key,比如更新的時候加上版本號
13.把版本號存儲在memcached中