之前向大家介紹過(guò)全球最大在線圖片服務(wù)網(wǎng)站Flickr網(wǎng)站架構(gòu)晓勇,Yupoo(又拍網(wǎng))作為國(guó)內(nèi)最大的圖片服務(wù)提供商慎框,我們也一起來(lái)看看它的架構(gòu)苦丁,同樣是提供圖片服務(wù)窥浪,看看他與Flickr的差別在哪里碟案,大家看完本文可以思考一下愿险。
一、先來(lái)看看Yupoo網(wǎng)站的基本信息:
帶寬:4000M/S (參考)
服務(wù)器數(shù)量:60 臺(tái)左右
Web服務(wù)器:Lighttpd, Apache, nginx
應(yīng)用服務(wù)器:Tomcat
其他:Python, Java, MogileFS 价说、ImageMagick 等
其架構(gòu)圖如下:
二辆亏、關(guān)于 Squid 與 Tomcat
Squid 與 Tomcat 似乎在 Web 2.0 站點(diǎn)的架構(gòu)中較少看到。我首先是對(duì) Squid 有點(diǎn)疑問(wèn)鳖目,對(duì)此阿華的解釋是"目前暫時(shí)還沒(méi)找到效率比 Squid 高的緩存系統(tǒng)扮叨,原來(lái)命中率的確很差,后來(lái)在 Squid 前又裝了層 Lighttpd, 基于 url 做 hash, 同一個(gè)圖片始終會(huì)到同一臺(tái) squid 去领迈,所以命中率徹底提高了"
對(duì)于應(yīng)用服務(wù)器層的 Tomcat彻磁,現(xiàn)在 Yupoo! 技術(shù)人員也在逐漸用其他輕量級(jí)的東西替代,而 YPWS/YPFS 現(xiàn)在已經(jīng)用 Python 進(jìn)行開(kāi)發(fā)了狸捅。
名次解釋?zhuān)?/p>
- YPWS--Yupoo Web Server YPWS 是用 Python開(kāi)發(fā)的一個(gè)小型 Web 服務(wù)器衷蜓,提供基本的 Web 服務(wù)外,可以增加針對(duì)用戶尘喝、圖片磁浇、外鏈網(wǎng)站顯示的邏輯判斷,可以安裝于任何有空閑資源的服務(wù)器中朽褪,遇到性能瓶頸時(shí)方便橫向擴(kuò)展置吓。
- YPFS--Yupoo File System 與 YPWS 類(lèi)似,YPFS 也是基于這個(gè) Web 服務(wù)器上開(kāi)發(fā)的圖片上傳服務(wù)器缔赠。
【Updated: 有網(wǎng)友留言質(zhì)疑 Python 的效率交洗,Yupoo 老大劉平陽(yáng)在 del.icio.us 上寫(xiě)到 "YPWS用Python自己寫(xiě)的,每臺(tái)機(jī)器每秒可以處理294個(gè)請(qǐng)求, 現(xiàn)在壓力幾乎都在10%以下"】
三橡淑、圖片處理層
接下來(lái)的 Image Process Server 負(fù)責(zé)處理用戶上傳的圖片构拳。使用的軟件包也是 ImageMagick,在上次存儲(chǔ)升級(jí)的同時(shí)梁棠,對(duì)于銳化的比率也調(diào)整過(guò)了(我個(gè)人感覺(jué)置森,效果的確好了很多)》”Magickd“ 是圖像處理的一個(gè)遠(yuǎn)程接口服務(wù)凫海,可以安裝在任何有空閑 CPU資源的機(jī)器上,類(lèi)似 Memcached的服務(wù)方式男娄。
我們知道 Flickr 的縮略圖功能原來(lái)是用 ImageMagick 軟件包的行贪,后來(lái)被雅虎收購(gòu)后出于版權(quán)原因而不用了(?)漾稀;EXIF 與 IPTC Flicke 是用 Perl 抽取的,我是非常建議 Yupoo! 針對(duì) EXIF 做些文章建瘫,這也是潛在產(chǎn)生受益的一個(gè)重點(diǎn)崭捍。
四、圖片存儲(chǔ)層
原來(lái) Yupoo! 的存儲(chǔ)采用了磁盤(pán)陣列柜啰脚,基于 NFS 方式的殷蛇,隨著數(shù)據(jù)量的增大,”Yupoo! 開(kāi)發(fā)部從07年6月份就開(kāi)始著手研究一套大容量的橄浓、能滿足 Yupoo! 今后發(fā)展需要的粒梦、安全可靠的存儲(chǔ)系統(tǒng)“,看來(lái) Yupoo! 系統(tǒng)比較有信心荸实,也是滿懷期待的匀们,畢竟這要支撐以 TB 計(jì)算的海量圖片的存儲(chǔ)和管理。我們知道准给,一張圖片除了原圖外昼蛀,還有不同尺寸的,這些圖片統(tǒng)一存儲(chǔ)在 MogileFS 中圆存。
對(duì)于其他部分叼旋,常見(jiàn)的 Web 2.0 網(wǎng)站必須軟件都能看到,如 MySQL沦辙、Memcached 夫植、Lighttpd 等。Yupoo! 一方面采用不少相對(duì)比較成熟的開(kāi)源軟件油讯,一方面也在自行開(kāi)發(fā)定制適合自己的架構(gòu)組件详民。這也是一個(gè) Web 2.0 公司所必需要走的一個(gè)途徑。
五陌兑、分庫(kù)設(shè)計(jì)
和很多使用MySQL的2.0站點(diǎn)一樣沈跨,又拍網(wǎng)的MySQL集群經(jīng)歷了從最初的一個(gè)主庫(kù)一個(gè)從庫(kù)、到一個(gè)主庫(kù)多個(gè)從庫(kù)兔综、 然后到多個(gè)主庫(kù)多個(gè)從庫(kù)的一個(gè)發(fā)展過(guò)程饿凛。
最初是由一臺(tái)主庫(kù)和一臺(tái)從庫(kù)組成,當(dāng)時(shí)從庫(kù)只用作備份和容災(zāi)软驰,當(dāng)主庫(kù)出現(xiàn)故障時(shí)涧窒,從庫(kù)就手動(dòng)變成主庫(kù),一般情況下锭亏,從庫(kù)不作讀寫(xiě)操作(同步除外)纠吴。 隨著壓力的增加,我們加上了memcached慧瘤,當(dāng)時(shí)只用其緩存單行數(shù)據(jù)戴已。 但是固该,單行數(shù)據(jù)的緩存并不能很好地解決壓力問(wèn)題,因?yàn)閱涡袛?shù)據(jù)的查詢通常很快糖儡。所以我們把一些實(shí)時(shí)性要求不高的Query放到從庫(kù)去執(zhí)行伐坏。后面又通過(guò)添加 多個(gè)從庫(kù)來(lái)分流查詢壓力,不過(guò)隨著數(shù)據(jù)量的增加休玩,主庫(kù)的寫(xiě)壓力也越來(lái)越大著淆。
在參考了一些相關(guān)產(chǎn)品和其它網(wǎng)站的做法后劫狠,我們決定進(jìn)行數(shù)據(jù)庫(kù)拆分拴疤。也就是將數(shù)據(jù)存放到不同的數(shù)據(jù)庫(kù)服務(wù)器中,一般可以按兩個(gè)緯度來(lái)拆分?jǐn)?shù)據(jù):
垂直拆分:是指按功能模塊拆分独泞,比如可以將群組相關(guān)表和照片相關(guān)表存放在不同的數(shù)據(jù)庫(kù)中呐矾,這種方式多個(gè)數(shù)據(jù)庫(kù)之間的表結(jié)構(gòu)不同。
水平拆分:而水平拆分是將同一個(gè)表的數(shù)據(jù)進(jìn)行分塊保存到不同的數(shù)據(jù)庫(kù)中懦砂,這些數(shù)據(jù)庫(kù)中的表結(jié)構(gòu)完全相同蜒犯。
拆分方式
一般都會(huì)先進(jìn)行垂直拆分,因?yàn)檫@種方式拆分方式實(shí)現(xiàn)起來(lái)比較簡(jiǎn)單荞膘,根據(jù)表名訪問(wèn)不同的數(shù)據(jù)庫(kù)就可以了罚随。但是垂直拆分方式并不能徹底解決所有壓力問(wèn) 題,另外羽资,也要看應(yīng)用類(lèi)型是否合適這種拆分方式淘菩。如果合適的話,也能很好的起到分散數(shù)據(jù)庫(kù)壓力的作用屠升。比如對(duì)于豆瓣我覺(jué)得比較適合采用垂直拆分潮改, 因?yàn)槎拱甑母骱诵臉I(yè)務(wù)/模塊(書(shū)籍、電影腹暖、音樂(lè))相對(duì)獨(dú)立汇在,數(shù)據(jù)的增加速度也比較平穩(wěn)。不同的是脏答,又拍網(wǎng)的核心業(yè)務(wù)對(duì)象是用戶上傳的照片糕殉,而照片數(shù)據(jù)的增 加速度隨著用戶量的增加越來(lái)越快。壓力基本上都在照片表上殖告,顯然垂直拆分并不能從根本上解決我們的問(wèn)題糙麦,所以,我們采用水平拆分的方式丛肮。
拆分規(guī)則
水平拆分實(shí)現(xiàn)起來(lái)相對(duì)復(fù)雜赡磅,我們要先確定一個(gè)拆分規(guī)則,也就是按什么條件將數(shù)據(jù)進(jìn)行切分宝与。 一般2.0網(wǎng)站都以用戶為中心焚廊,數(shù)據(jù)基本都跟隨用戶冶匹,比如用戶的照片、朋友和評(píng)論等等咆瘟。因此一個(gè)比較自然的選擇是根據(jù)用戶來(lái)切分嚼隘。每個(gè)用戶都對(duì)應(yīng)一個(gè)數(shù)據(jù) 庫(kù),訪問(wèn)某個(gè)用戶的數(shù)據(jù)時(shí)袒餐, 我們要先確定他/她所對(duì)應(yīng)的數(shù)據(jù)庫(kù)飞蛹,然后連接到該數(shù)據(jù)庫(kù)進(jìn)行實(shí)際的數(shù)據(jù)讀寫(xiě)。
那么灸眼,怎么樣對(duì)應(yīng)用戶和數(shù)據(jù)庫(kù)呢卧檐?我們有這些選擇:
按算法對(duì)應(yīng)
最簡(jiǎn)單的算法是按用戶ID的奇偶性來(lái)對(duì)應(yīng),將奇數(shù)ID的用戶對(duì)應(yīng)到數(shù)據(jù)庫(kù)A焰宣,而偶數(shù)ID的用戶則對(duì)應(yīng)到數(shù)據(jù)庫(kù)B霉囚。這個(gè)方法的最大問(wèn)題是,只能分成兩 個(gè)庫(kù)匕积。另一個(gè)算法是按用戶ID所在區(qū)間對(duì)應(yīng)盈罐,比如ID在0-10000之間的用戶對(duì)應(yīng)到數(shù)據(jù)庫(kù)A, ID在10000-20000這個(gè)范圍的對(duì)應(yīng)到數(shù)據(jù)庫(kù)B闪唆,以此類(lèi)推盅粪。按算法分實(shí)現(xiàn)起來(lái)比較方便,也比較高效悄蕾,但是不能滿足后續(xù)的伸縮性要求票顾,如果需要增加 數(shù)據(jù)庫(kù)節(jié)點(diǎn),必需調(diào)整算法或移動(dòng)很大的數(shù)據(jù)集笼吟, 比較難做到在不停止服務(wù)的前提下進(jìn)行擴(kuò)充數(shù)據(jù)庫(kù)節(jié)點(diǎn)库物。
按索引/映射表對(duì)應(yīng)
這種方法是指建立一個(gè)索引表,保存每個(gè)用戶的ID和數(shù)據(jù)庫(kù)ID的對(duì)應(yīng)關(guān)系贷帮,每次讀寫(xiě)用戶數(shù)據(jù)時(shí)先從這個(gè)表獲取對(duì)應(yīng)數(shù)據(jù)庫(kù)戚揭。新用戶注冊(cè)后,在所有可用 的數(shù)據(jù)庫(kù)中隨機(jī)挑選一個(gè)為其建立索引撵枢。這種方法比較靈活民晒,有很好的伸縮性。一個(gè)缺點(diǎn)是增加了一次數(shù)據(jù)庫(kù)訪問(wèn)锄禽,所以性能上沒(méi)有按算法對(duì)應(yīng)好潜必。
比較之后,我們采用的是索引表的方式沃但,我們?cè)敢鉃槠潇`活性損失一些性能磁滚,更何況我們還有memcached, 因?yàn)樗饕龜?shù)據(jù)基本不會(huì)改變的緣故,緩存命中率非常高垂攘。所以能很大程度上減少了性能損失维雇。