發(fā)布時(shí)間:2011-09-07 8:06:58 作者:957515135@qq.com 閱讀次數(shù):56
Flickr.com 是網(wǎng)上最受歡迎的照片共享網(wǎng)站之一,還記得那位給Windows Vista拍攝壁紙的HamadDarwish嗎?他就是將照片上傳到Flickr,后而被微軟看中成為Vista壁紙御用攝影師。
Flickr.com 是最初由位于溫哥華的Ludicorp公司開發(fā)設(shè)計(jì)并于2004年2月正式發(fā)布的,由于大量應(yīng)用了WEB2.0技術(shù),注重用戶體驗(yàn),使得其迅速獲得了大量的用戶,2007年11月,F(xiàn)lickr迎來了第20億張照片,一年后,這個(gè)數(shù)字就達(dá)到了30億,并且還在以加速度增長(zhǎng)。2005年3月,雅虎公司以3千500萬美元收購(gòu)了Ludicorp公司和Flickr.com。雖然Flickr并不是最大的照片共享網(wǎng)站(Facebook以超過100億張照片排名第一),但這筆收購(gòu)仍然被認(rèn)為是WEB2.0浪潮中最精明的收購(gòu),因?yàn)閮H僅一年后,Google就以16億美元的高價(jià)收購(gòu)了YouTube,而2007年10月,微軟斥資2.4億美元收購(gòu) Facebook1.6%股份,此舉使Facebook估值高達(dá)150億美元。估計(jì)Ludicorp公司的創(chuàng)始人Stewart Butterfield和CaterinaFake夫婦現(xiàn)在還在后悔吧。
在2005年溫哥華PHP協(xié)會(huì)的簡(jiǎn)報(bào)以及隨后的一系列會(huì)議上,F(xiàn)lickr的架構(gòu)師CalHenderson公開了大部分Flickr所使用的后臺(tái)技術(shù),使得我們能有機(jī)會(huì)來分享和研究其在構(gòu)建可擴(kuò)展Web站點(diǎn)的經(jīng)驗(yàn)。本文大部分資料來自互聯(lián)網(wǎng)和自己的一點(diǎn)點(diǎn)心得,歡迎大家參與討論,要是能夠起到拋磚引玉的作用,本人將不勝榮幸。
Flickr整體框架圖
在討論Flickr 網(wǎng)站架構(gòu)之前,讓我們先來看一組統(tǒng)計(jì)數(shù)據(jù)(數(shù)據(jù)來源:April 2007 MySQL Conf andExpo和Flickr網(wǎng)站)
每天多達(dá)40億次的查詢請(qǐng)求
squid總計(jì)約有3500萬張照片(硬盤+內(nèi)存)
squid內(nèi)存中約有200萬張照片
總計(jì)有大約4億7000萬張照片,每張圖片又生成不同尺寸大小的4-5份圖片
每秒38,000次Memcached請(qǐng)求 (Memcached總共存儲(chǔ)了1200萬對(duì)象)
超過2 PB 存儲(chǔ),其中數(shù)據(jù)庫12TB
每天新增圖片超過 40萬(周日峰值超過200萬,約1.5TB)
超過8百50萬注冊(cè)用戶
超過1千萬的唯一標(biāo)簽(tags)
響應(yīng)4萬個(gè)照片訪問請(qǐng)求
處理10萬個(gè)緩存操作
運(yùn)行13萬個(gè)數(shù)據(jù)庫查詢
這張是Flickr的網(wǎng)站架構(gòu)圖,我們這里只作一些簡(jiǎn)要的描述,具體的分析請(qǐng)靜待后續(xù)文章。
Pair of ServerIron's做負(fù)載均衡方案
Squid Caches 代理,用于緩存靜態(tài)的HTML和照片
Net App公司的Filer, NAS存儲(chǔ)設(shè)備,用于存儲(chǔ)照片
PHP App Servers
- 運(yùn)行REDHAT LINUX,Apache上的PHP應(yīng)用,F(xiàn)lickr網(wǎng)站的主體是大約6萬行PHP代碼
- 沒有使用PHP session, 應(yīng)用是stateless,便于擴(kuò)展,并避免PHP Server故障所帶來的Session失效。
- 每個(gè)頁面有大約27~35個(gè)查詢(不知道為什么這么設(shè)計(jì),個(gè)人覺得沒有必要)
- 另有專門的Apache Web Farm 服務(wù)于靜態(tài)文件(HTML和照片)的訪問
Storage Manager 運(yùn)行私有的,適用于海量文件存儲(chǔ)的Flickr File System
Dual Tree Central Database
- MySQL 數(shù)據(jù)庫,存放用戶表,記錄的信息是用戶主鍵以及此用戶對(duì)以的數(shù)據(jù)庫Shard區(qū),從中心用戶表中查出用戶數(shù)據(jù)所在位置,然后直接從目標(biāo)Shard中取出數(shù)據(jù)。
- “Dual Tree"架構(gòu)是”Master-Master"和“Master-Slave"的有效結(jié)合,雙Master 避免了“單點(diǎn)故障”,Master-Slave又提高了讀取速度,因?yàn)橛脩舯淼牟僮?0%以上是讀。
Master-master shards
- MySQL 數(shù)據(jù)庫,存儲(chǔ)實(shí)際的用戶數(shù)據(jù)和照片的元數(shù)據(jù)(Meta Data),每個(gè)Shard 大約40萬個(gè)用戶,120GB 數(shù)據(jù)。每個(gè)用戶的所有數(shù)據(jù)存放在同一個(gè)shard中。
- Shard中的每一個(gè)server的負(fù)載只是其可最大負(fù)載的50%,這樣在需要的時(shí)候可以O(shè)nline停掉一半的server進(jìn)行升級(jí)或維護(hù)而不影響系統(tǒng)性能。
- 為了避免跨Shard查詢所帶來的性能影響,一些數(shù)據(jù)有在不同的Shard有兩份拷貝,比如用戶對(duì)照片的評(píng)論,通過事務(wù)來保證其同步。
Memcached Cluster 中間層緩存服務(wù)器,用于緩存數(shù)據(jù)庫的SQL查詢結(jié)果等。
Big Search Engine
- 復(fù)制部分Shard數(shù)據(jù)(Owner’s single tag)到Search Engine Farm以響應(yīng)實(shí)時(shí)的全文檢索。
- 其他全文檢索請(qǐng)求利用Yahoo的搜索引擎處理(Flickr是Yahoo旗下的公司
)
服務(wù)器的硬件配置:
- Intel或AMD 64位CPU,16GB RAM
- 6-disk 15K RPM RAID-10.
- 2U boxes.
服務(wù)器數(shù)量:(數(shù)據(jù)來源:April 2008 MySQL Conference & Expo)
166 DB servers, 244 web servers(不知道是否包括 squid server?), 14 Memcached servers
數(shù)據(jù)庫最初的擴(kuò)展-Replication
也許有人不相信,不過Flickr確實(shí)是從一臺(tái)服務(wù)器起步的,即Apache/PHP和MySQL是運(yùn)行在同一臺(tái)服務(wù)器上的,很快MySQL服務(wù)器就獨(dú)立了出來,成了雙服務(wù)器架構(gòu)。隨著用戶和訪問量的快速增長(zhǎng),MySQL數(shù)據(jù)庫開始承受越來越大的壓力,成為應(yīng)用瓶頸,導(dǎo)致網(wǎng)站應(yīng)用響應(yīng)速度變慢,MySQL的擴(kuò)展問題就擺在了Flickr的技術(shù)團(tuán)隊(duì)面前。
不幸的是,在當(dāng)時(shí),他們的選擇并不多。一般來說,數(shù)據(jù)庫的擴(kuò)展無外是兩條路,Scale-Up和Scale-Out,所謂Scale-Up,簡(jiǎn)單的說就是在同一臺(tái)機(jī)器內(nèi)增加CPU,內(nèi)存等硬件來增加數(shù)據(jù)庫系統(tǒng)的處理能力,一般不需要修改應(yīng)用程序;而Scale-Out,就是我們通常所說的數(shù)據(jù)庫集群方式,即通過增加運(yùn)行數(shù)據(jù)庫服務(wù)器的數(shù)量來提高系統(tǒng)整體的能力,而應(yīng)用程序則一般需要進(jìn)行相應(yīng)的修改。在常見的商業(yè)數(shù)據(jù)庫中,Oracle具有很強(qiáng)的Scale-Up的能力,很早就能夠支持幾十個(gè)甚至數(shù)百個(gè)CPU,運(yùn)行大型關(guān)鍵業(yè)務(wù)應(yīng)用;而微軟的SQLSERVER,早期受Wintel架構(gòu)所限,以Scale-Out著稱,但自從幾年前突破了Wintel體系架構(gòu)8路CPU的的限制,Scale-Up的能力一路突飛猛進(jìn),最近更是發(fā)布了SQL 2008在Windows 2008R2版運(yùn)行256個(gè)CPU核心(core)的測(cè)試結(jié)果,開始挑戰(zhàn)Oracle的高端市場(chǎng)。而MySQL,直到今年4月,在最終采納了GOOGLE公司貢獻(xiàn)的SMP性能增強(qiáng)的代碼后,發(fā)布了MySQL5.4后,才開始支持16路CPU的X86系統(tǒng)和64路CPU的CMT系統(tǒng)(基于Sun UltraSPARC的系統(tǒng))。
從另一方面來說,Scale-Up受軟硬件體系的限制,不可能無限增加CPU和內(nèi)存,相反Scale-Out卻是可以"幾乎"無限的擴(kuò)展,以Google為例,2006年Google一共有超過45萬臺(tái)服務(wù)器(誰能告訴我現(xiàn)在他們有多少??。?;而且大型SMP服務(wù)器的價(jià)格遠(yuǎn)遠(yuǎn)超過普通的雙路服務(wù)器,對(duì)于很多剛剛起步或是業(yè)務(wù)增長(zhǎng)很難預(yù)測(cè)的網(wǎng)站來說,不可能也沒必要一次性投資購(gòu)買大型的硬件設(shè)備,因而雖然Scale-Out會(huì)隨著服務(wù)器數(shù)量的增多而帶來管理,部署和維護(hù)的成本急劇上升,但確是大多數(shù)大型網(wǎng)站當(dāng)然也包括Flickr的唯一選擇。
經(jīng)過統(tǒng)計(jì),F(xiàn)lickr的技術(shù)人員發(fā)現(xiàn),查詢即SELECT語句的數(shù)量要遠(yuǎn)遠(yuǎn)大于添加,更新和
刪除的數(shù)量,比例達(dá)到了大約13:1甚至更多,所以他們采用了“Master-Slave”的復(fù)制模式,即所有的“寫”操作都在發(fā)生在“Master",然后”異步“復(fù)制到一臺(tái)或多臺(tái)“Slave"上,而所有的”讀“操作都轉(zhuǎn)到”Slave"上運(yùn)行,這樣隨著“讀”交易量的增加,只需增加Slave服務(wù)器 就可以了。
讓我們來看一下應(yīng)用系統(tǒng)應(yīng)該如何修改來適應(yīng)這樣的架構(gòu),除了”讀/寫“分離外,對(duì)于”讀“操作最基本的要求是:1)應(yīng)用程序能夠在多個(gè)”Slave“上進(jìn)行負(fù)載均分;2)當(dāng)一個(gè)或多個(gè)”slave"出現(xiàn)故障時(shí),應(yīng)用程序能自動(dòng)嘗試下一個(gè)“slave”,如果全部“Slave"失效,則返回錯(cuò)誤。Flickr曾經(jīng)考慮過的方案是在Web應(yīng)用和”Slave“群之間加入一個(gè)硬件或軟件的”Load Balancer“,如下圖
這樣的好處是應(yīng)用所需的改動(dòng)最小,因?yàn)閷?duì)于應(yīng)用來說,所有的讀操作都是通過一個(gè)虛擬的Slave來進(jìn)行,添加和刪除“Slave"服務(wù)器對(duì)應(yīng)用透明,Load Balancer實(shí)現(xiàn)對(duì)各個(gè)Slave服務(wù)器狀態(tài)的監(jiān)控并將出現(xiàn)故障的Slave從可用節(jié)點(diǎn)列表里刪除,并可以實(shí)現(xiàn)一些復(fù)雜的負(fù)載分擔(dān)策略,比如新買的服務(wù)器處理能力要高過Slave群中其他的老機(jī)器,那么我們可以給這個(gè)機(jī)器多分配一些負(fù)載以最有效的利用資源。一個(gè)簡(jiǎn)單的利用Apacheproxy_balancer_module的例子如下:
1
2
3
4
5
6
7
8
9
10
11
。。。。。。。。。。。。。。
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_http_module modules/mod_proxy_http.so
。。。。。。。。。。。。。。。。。。。。
<Proxy
balancer://mycluster>
BalancerMember "
http://slave1:8008/App" loadfactor=4
BalancerMember "
http://slave2:8008/App" loadfactor=3
BalancerMember "
http://slave3:8008/App" loadfactor=3
....................
///slave load ratio 4:3:3.
最終,F(xiàn)lickr采用了一種非?!拜p量”但有效的“簡(jiǎn)易”PHP實(shí)現(xiàn),基本的代碼只有10幾行:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function db_connect($hosts, $user, $pass){
shuffle($hosts); //shuffle()是PHP函數(shù),作用是將數(shù)組中每個(gè)元素的順序隨機(jī)打亂。
foreach($hosts as $host){
debug("Trying to connect to $host...");
$dbh = @mysql_connect($host, $user, $pass, 1);
if ($dbh){
debug("Connected to $host!");
return $dbh;
}
debug("Failed to connect to $host!");
}
debug("Failed to connect to all hosts in list - giving up!");
return 0;
}
在上述代碼中,如果需要對(duì)特定的Slave賦予更高的負(fù)載,只要在$hosts中多出現(xiàn)一次或多次就可以了。這段代碼只要稍稍改進(jìn),就可以實(shí)現(xiàn)更復(fù)雜的功能,如當(dāng)connect失敗時(shí)自動(dòng)將host從hosts列表中去除等。
“Master”-"Slave"模式的缺點(diǎn)是它并沒有對(duì)于“寫'操作提供擴(kuò)展能力,而且存在單點(diǎn)故障,即一旦Master故障,整個(gè)網(wǎng)站將喪失“更新”的能力。解決的辦法采用“Master"-"Master"模式,即兩臺(tái)服務(wù)器互為”Master“-"Slave",這樣不僅”讀/寫“能力擴(kuò)展了一倍,而且有效避免了”單點(diǎn)故障“,結(jié)合已有的“Master"-"Slave",整個(gè)數(shù)據(jù)庫的架構(gòu)就變成了下面的”雙樹“結(jié)構(gòu),
。
“雙樹”架構(gòu)并沒有支撐太久的時(shí)間,大概6個(gè)月后,隨著用戶的激增,系統(tǒng)再一次達(dá)到了極限,不僅”寫”操作成為了瓶頸,而且“異步復(fù)制"也由于”Slave“服務(wù)器過于繁忙而出現(xiàn)了嚴(yán)重的滯后而造成讀數(shù)據(jù)的不一致。那么,能不能在現(xiàn)有架構(gòu)加以解決,比如說增加新的”Master“服務(wù)器和考慮采用”同步復(fù)制“呢?答案是否定的,在Master超過兩臺(tái)的設(shè)置中,只能采用”閉環(huán)鏈“的方式進(jìn)行復(fù)制,在大數(shù)據(jù)量的生產(chǎn)環(huán)境中,很容易造成在任意時(shí)刻沒有一個(gè)Master或Slave節(jié)點(diǎn)是具有全部最新數(shù)據(jù)的(有點(diǎn)類似于”人一次也不能踏進(jìn)同一條河“?),這樣很難保障數(shù)據(jù)的一致性,而且一旦其中一個(gè)Master出現(xiàn)故障,將中斷整個(gè)復(fù)制鏈;而對(duì)于”同步復(fù)制“,當(dāng)然這是消除”復(fù)制滯后“的最好辦法,不過在當(dāng)時(shí)MySQL的同步復(fù)制還遠(yuǎn)沒有成熟到可以運(yùn)用在投產(chǎn)環(huán)境中。
Flickr網(wǎng)站的架構(gòu),需要一次大的變化來解決長(zhǎng)期持續(xù)擴(kuò)展的問題。
Shard - 大型網(wǎng)站數(shù)據(jù)庫擴(kuò)展的終極武器?
2005年7月,另一位大牛(MySQL 2005、2006年度 "Application of the Year Award"獲得者)DathanPattishall加入了Flickr團(tuán)隊(duì)。一個(gè)星期之內(nèi),Dathan解決了Flickr數(shù)據(jù)庫40%的問題,更重要的是,他為Flickr引進(jìn)了Shard架構(gòu),從而使Flickr網(wǎng)站具備了真正“線性”Scale-Out的增長(zhǎng)能力,并一直沿用至今,取得了巨大的成功。
Shard主要是為了解決傳統(tǒng)數(shù)據(jù)庫Master/Slave模式下單一Master數(shù)據(jù)庫的“寫”瓶頸而出現(xiàn)的,簡(jiǎn)單的說Shard就是將一個(gè)大表分割成多個(gè)小表,每個(gè)小表存儲(chǔ)在不同機(jī)器的數(shù)據(jù)庫上,從而將負(fù)載分散到多個(gè)機(jī)器并行處理而極大的提高整個(gè)系統(tǒng)的“寫”擴(kuò)展能力。相比傳統(tǒng)方式,由于每個(gè)數(shù)據(jù)庫都相對(duì)較小,不僅讀寫操作更快,甚至可以將整個(gè)小數(shù)據(jù)庫緩存到內(nèi)存中,而且每個(gè)小數(shù)據(jù)庫的備份,恢復(fù)也變得相對(duì)容易,同時(shí)由于分散了風(fēng)險(xiǎn),單個(gè)小數(shù)據(jù)庫的故障不會(huì)影響其他的數(shù)據(jù)庫,使整個(gè)系統(tǒng)的可靠性也得到了顯著的提高。
對(duì)于大多數(shù)網(wǎng)站來說,以用戶為單位進(jìn)行Shard分割是最合適不過的,常見的分割方法有按地域(比如郵編),按Key值(比如Hash用戶ID),這些方法可以簡(jiǎn)單的通過應(yīng)用配置文件或算法來實(shí)現(xiàn),一般不需要另外的數(shù)據(jù)庫,缺點(diǎn)是一旦業(yè)務(wù)增加,需要再次分割Shard時(shí)要修改現(xiàn)有的應(yīng)用算法和重新計(jì)算所 有的ShardKEY值;而最為靈活的做法是以“目錄”服務(wù)為基礎(chǔ)的分割,即在Shard之前加一個(gè)中央數(shù)據(jù)庫(Global LookupCluster),應(yīng)用要先根據(jù)用戶主鍵值查詢中央數(shù)據(jù)庫,獲得用戶數(shù)據(jù)所在的Shard,隨后的操作再轉(zhuǎn)向Shard所在數(shù)據(jù)庫,例如下圖:
而應(yīng)用的主要修改在于要添加一個(gè)Lookup訪問層,例如將以下的代碼:
1
2
3
string connectionString = @"Driver={MySQL};SERVER=dbserver;DATABASE=CustomerDB;";
OdbcConnection conn = new OdbcConnection(connectionString);
conn.Open();
變?yōu)椋?div style="height:15px;">
1
2
3
string connectionString = GetDatabaseFor(customerId);
OdbcConnection conn = new OdbcConnection(connectionString);
conn.Open();
GetDatabaseFor()函數(shù)完成根據(jù)用戶ID獲取ShardconnectionString的作用。
對(duì)應(yīng)我們前面所提到過的Flickr架構(gòu)
DualTree Central Database就是中央數(shù)據(jù)庫,存放用戶表,記錄的信息是用戶主鍵以及此用戶對(duì)以的數(shù)據(jù)庫Shard區(qū);而Master-MasterShards就是一個(gè)個(gè)的Shard用戶數(shù)據(jù)庫,存儲(chǔ)實(shí)際的用戶數(shù)據(jù)和照片的元數(shù)據(jù)(Meta Data)。
Flickr Shard的設(shè)計(jì)我們?cè)贔lickr網(wǎng)站架構(gòu)研究(1)中已經(jīng)總結(jié)過了,在此不再贅述。我們?cè)诖苏勔幌耂hard架構(gòu)的主要問題和Flickr的解決辦法:1)Shard只適用于不需要join操作的表,因?yàn)榭鏢hard join操作的開銷太大,解決的辦法是將一個(gè)用戶的所有數(shù)據(jù)全部存放在同一個(gè)Shard里,對(duì)于一些傳統(tǒng)方式下需要跨Shard查詢的數(shù)據(jù),只能采取冗余的方法,比如Shard1的用戶A對(duì)Shard2的用戶B的照片進(jìn)行了評(píng)論,那么這條評(píng)論將同時(shí)存放在Shard1和Shard2中。這樣就存在一個(gè)數(shù)據(jù)一致性的問題,常規(guī)的做法是用數(shù)據(jù)庫事務(wù)(Transaction)、”兩階段提交“(2 phasecommit)來解決,但做過兩階段提交(2PC)應(yīng)用的都知道,2PC的效率相對(duì)較差,而且實(shí)際上也不能100%保證數(shù)據(jù)的完整性和一致性;另外,一旦由于其中一個(gè)Shard故障而提交失敗回滾,用戶只能放棄或再試一遍,用戶體驗(yàn)較差。Flickr對(duì)于數(shù)據(jù)一致性的解決方案是Queue(Flickr用PHP開發(fā)了一個(gè)強(qiáng)大的Queue系統(tǒng),將所有可以異步的任務(wù)都用Queue來實(shí)現(xiàn),每天處理高達(dá)1千萬以上的任務(wù)。),事實(shí)上當(dāng)用戶A對(duì)用戶B的照片進(jìn)行評(píng)論時(shí),他并不關(guān)心這條評(píng)論什么時(shí)候出現(xiàn)在用戶B的界面上,即將這條評(píng)論添加到用戶B的交易是可以異步的,允許一定的遲延,通過Queue處理,既保證了數(shù)據(jù)的一致性,又縮短了用戶端的相應(yīng)時(shí)間,提高了系統(tǒng)性能。2)Shard的另一個(gè)主要問題Rebalancing,既當(dāng)現(xiàn)有Shard的負(fù)載達(dá)到一定的閥值,如何將現(xiàn)有數(shù)據(jù)再次分割,F(xiàn)lickr目前的方式依然是手工的,既人工來確定哪些用戶需要遷移,然后運(yùn)行一個(gè)后臺(tái)程序進(jìn)行數(shù)據(jù)遷移,遷移的過程用戶賬戶將被鎖住。(據(jù)說Google做到了完全自動(dòng)的Rebalancing,本著”薩大“坑里不再挖坑的原則,如果有機(jī)會(huì)的話,留到下一個(gè)系列再研究 吧)
Memcached的應(yīng)用和爭(zhēng)論
大家應(yīng)該已經(jīng)注意到,F(xiàn)lickr為中央數(shù)據(jù)庫配置了Memcached作為數(shù)據(jù)庫緩存,接下來的問題是,為什么用Memcached?為什么Shard不需要Memcached?Memcached和Master,Slave的關(guān)系怎樣?筆者將試圖回答這些問題供大家參考,網(wǎng)上的相關(guān)爭(zhēng)論很多,有些問題尚未有定論。
Memecached是一個(gè)高性能的,分布式的,開源的內(nèi)存對(duì)象緩存系統(tǒng),顧名思義,它的主要目的是將經(jīng)常讀取的對(duì)象放入內(nèi)存以提高整個(gè)系統(tǒng),尤其是數(shù)據(jù)庫的擴(kuò)展能力。Memcached的主要結(jié)構(gòu)是兩個(gè)Hash Table,Server端的HashTable以key-valuepair的方式存放對(duì)象值,而Client端的HashTable的則決定某一對(duì)象存放在哪一個(gè)MemcachedServer.舉個(gè)例子說,后臺(tái)有3個(gè)MemecachedServer,A、B、C,Client1需要將一個(gè)對(duì)象名為”userid123456“,值為“魯丁"的存入,經(jīng)過Client1的Hash計(jì)算,"userid123456"的值應(yīng)該放入Memcached ServerB,而這之后,Client2需要讀取"userid123456"的值,經(jīng)過同樣的Hash計(jì)算,得出"userid123456"的值如果存在的話應(yīng)該在Memcached ServerB,并從中取出。最妙的是Server之間彼此是完全獨(dú)立的,完全不知道對(duì)方的存在,沒有一個(gè)類似與Master或AdminServer的存在,增加和減少Server只需在Client端"注冊(cè)"并重新Hash就可以了。
Memcached作為數(shù)據(jù)庫緩存的作用主要在于減輕甚至消除高負(fù)載數(shù)據(jù)庫情況下頻繁讀取所帶來的DiskI/O瓶頸,相對(duì)于數(shù)據(jù)庫自身的緩存來說,具有以下優(yōu)點(diǎn):1)Memecached的緩存是分布式的,而數(shù)據(jù)庫的緩存只限于本機(jī);2)Memcached緩存的是對(duì)象,可以是經(jīng)過復(fù)雜運(yùn)算和查詢的最終結(jié)果,并且不限于數(shù)據(jù),可以是任何小于1MB的對(duì)象,比如html文件等;而數(shù)據(jù)庫緩存是以"row"為單位的,一旦"row"中的任何數(shù)據(jù)更新,整個(gè)“row"將進(jìn)行可能是對(duì)應(yīng)用來說不必要的更新;3)Memcached的存取是輕量的,而數(shù)據(jù)庫的則相對(duì)較重,在低負(fù)載的情況下,一對(duì)一的比較,Memcached的性能未必能超過數(shù)據(jù)庫,而在高負(fù)載的情況下則優(yōu)勢(shì)明顯。
Memcached并不適用于更新頻繁的數(shù)據(jù),因?yàn)轭l繁更新的數(shù)據(jù)導(dǎo)致大量的Memcached更新和較低的緩沖命中率,這可能也是為什么Shard沒有集成它的原因;Memcached更多的是擴(kuò)展了數(shù)據(jù)庫的”讀“操作,這一點(diǎn)上它和Slave的作用有重疊,以至于有人爭(zhēng)論說應(yīng)該讓"Relication"回到它最初的目的”O(jiān)nlineBackup"數(shù)據(jù)庫上,而通過Memcached來提供數(shù)據(jù)庫的“讀”擴(kuò)展。(當(dāng)然也有人說,考慮到Memcached的對(duì)應(yīng)用帶來的復(fù)雜性,還是慎用。)
然而,在體系架構(gòu)中增加Memecached并不是沒有代價(jià)的,現(xiàn)有的應(yīng)用要做適當(dāng)?shù)男薷膩硗組emcached和數(shù)據(jù)庫中的數(shù)據(jù),同時(shí)Memcached不提供任何冗余和“failover”功能,這些復(fù)雜的控制都需要應(yīng)用來實(shí)現(xiàn)。基本的應(yīng)用邏輯如下:
對(duì)于讀操作:
1
2
3
4
5
$data = memcached_fetch( $id );
return $data if $data
$data = db_fetch( $id );
memcached_store( $id, $data );
return $data;
對(duì)于寫操作:
1
2
db_store( $id, $data );
memcached_store( $id, $data );
我們看到在每一次數(shù)據(jù)更新都需要更新Memcached,而且數(shù)據(jù)庫或Memcached任何一點(diǎn)寫錯(cuò)誤應(yīng)用就可能取得“過期”的數(shù)據(jù)而得到錯(cuò)誤的結(jié)果,如何保證數(shù)據(jù)庫和Memcached的同步呢?
復(fù)制滯后和同步問題的解決
我們知道復(fù)制滯后的主要原因是數(shù)據(jù)庫負(fù)載過大而造成異步復(fù)制的延遲,Shard架構(gòu)有效的分散了系統(tǒng)負(fù)載,從而大大減輕了這一現(xiàn)象,但是并不能從根本上消除,解決這一問題還是要靠良好的應(yīng)用設(shè)計(jì)。
當(dāng)用戶訪問并更新Shard數(shù)據(jù)時(shí),F(xiàn)lickr采用了將用戶“粘”到某一機(jī)器的做法,
$id= intval(substr($user_id, -10));
$id %$count_of_hosts_in_shard
即同一用戶每次登錄的所有操作其實(shí)都是在Shard中的一個(gè)Master上運(yùn)行的,這樣即使復(fù)制到Slave,也就是另一臺(tái)Master的時(shí)候有延時(shí),也不會(huì)對(duì)用戶有影響,除非是用戶剛剛更新,尚未復(fù)制而這臺(tái)Master就出現(xiàn)故障了,不過這種幾率應(yīng)該很小吧。
對(duì)于CentralDatabase的復(fù)制滯后和同步問題,F(xiàn)lickr采用了一種復(fù)雜的“Write Through Cache"的機(jī)制來處理:
"WriteThrough Cache"就是將所有的數(shù)據(jù)庫”寫“操作都先寫入”Cache",然后由Cache統(tǒng)一去更新數(shù)據(jù)庫的各個(gè)Node,“Write ThroughCache"維護(hù)每一個(gè)Node的更新狀態(tài),當(dāng)有讀請(qǐng)求時(shí),即將請(qǐng)求轉(zhuǎn)向狀態(tài)為”已同步“的Node,這樣即避免了復(fù)制滯后和Memcached的同步問題,但缺點(diǎn)是其實(shí)現(xiàn)極為復(fù)雜,“Write ThrougCache"層的代碼需要考慮和實(shí)現(xiàn)所有”journal","Transaction“,“failover”,和“recovery"這些數(shù)據(jù)庫已經(jīng)實(shí)現(xiàn)的功能,另外還要考慮自身的"failover"問題。我沒有找到有關(guān)具體實(shí)現(xiàn)的說明,只能猜測(cè)這一部分的處理可能也是直接利用或是實(shí)現(xiàn)了類似于Flickr的Queue系統(tǒng)吧。