中等規(guī)模網(wǎng)站的UGC圖片存放規(guī)劃

先聲明,本文借鑒了很多劉濤(Tarkus)和 Druggo Yang 的實(shí)戰(zhàn)經(jīng)驗(yàn),特此感謝
好像現(xiàn)在是個(gè)網(wǎng)站就允許用戶上傳頭像,其中一部分還允許上傳相冊(cè)、個(gè)性背景圖之類的東西。對(duì)圖片的規(guī)劃各村都有各村的高招,這里只是拋磚引玉、提個(gè)醒:當(dāng)文件膨脹到一定規(guī)模的時(shí)候再去改就來不及了,在一個(gè)項(xiàng)目的草創(chuàng)時(shí)期,讓一個(gè)人多花兩個(gè)星期的時(shí)間來琢磨這個(gè)“小”問題也絕對(duì)稱不上是過度設(shè)計(jì)。
我對(duì)中等的定義:圖片所占空間在 1T – 數(shù)十 T 之間。
功能需求
基本的就兩點(diǎn):排除重復(fù),和可擴(kuò)展性。
排重并不為很多人所重視,因?yàn)閷?duì)很多人來說短期可以承受,實(shí)際經(jīng)驗(yàn)重復(fù)的占了 50%(一些流行的圖片會(huì)被重復(fù)上傳很多次),但問題是這里還關(guān)系到另外一件事:審核。例如一些很流行的黃圖會(huì)被頻繁的被不同人重復(fù)上傳,這也是不小的審核工作量。
具體設(shè)計(jì)
簡(jiǎn)單的說就是 MySQL 單點(diǎn)來保證唯一,將文件 MD5 轉(zhuǎn)換為遞增ID,再將固定數(shù)量的文件分成組,例如最簡(jiǎn)單的 MySQL 表可以這樣。
CREATE TABLE IF NOT EXISTS `upload_pic` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`hash` binary(16) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `hash` (`hash`)
) ENGINE=MyISAM DEFAULT CHARSET=ucs2 AUTO_INCREMENT=1;
每次有上傳,先 MD5、INSERT,成功就返回 ID,失敗就重新 SELECT 找以前的 ID
文件就是整個(gè) ID 的補(bǔ)零切三段,如 12345 保存為實(shí)際的文件 /upload/000/012/345,不保留擴(kuò)展名,統(tǒng)一發(fā)送 Header Content-Type application/octet-stream
首要原則是只新增,不改寫(除了審核后刪除的)
調(diào)優(yōu)
上面所述只為了方便理解的最精簡(jiǎn)的方案,還有很多事情可以做。
用于發(fā)號(hào)的 MySQL 僅僅是寫入單點(diǎn),對(duì)于已經(jīng)生成的 hash,可以定期生成?Archive 引擎 的只讀表。然后新上傳時(shí)以 SELECT/INSERT/SELECT 的順序獲取 ID(這時(shí)候,第三步的 SELECT 是極其罕見的)。
如果是 nfs 掛載,初期可以掛整個(gè) /upload,后期可以以一級(jí)子目錄為單位,分散在不同機(jī)器上。如 /upload/001 和 /upload/002 掛在機(jī)器 A 上,諸如此類。這樣我們可以每 M 個(gè)圖片為一個(gè)掛載單位(下面簡(jiǎn)稱 MU——Mounted Unit)。如果圖片的平均大小是 200K 左右,則每個(gè) MU 的平均大小應(yīng)該在 1M * 200K = 200G 左右。由于內(nèi)容不再改寫,老的 MU 可以到處搬動(dòng)、甚至分散到不同機(jī)房,其實(shí)是類似 flickr 的 farm*.static 子域名。
實(shí)際磁盤問題是個(gè)大問題,這方面網(wǎng)友 Druggo Yang 給了我很多經(jīng)驗(yàn),他最早面臨的問題是大量隨機(jī)讀寫、幾乎沒熱點(diǎn),大概是文件實(shí)在太碎,硬盤反倒在網(wǎng)卡之前成為瓶頸,銀彈是有:SSD,不過太tmd貴了。最后他們是用的 LVS,那已經(jīng)是我知識(shí)體系以外的東西了。
我想到的解決方法被 Druggo Yang 評(píng)價(jià)為自己做軟 RAID:不同 MU 掛到不同盤,用 nginx 訪問,或者系統(tǒng)本身瓶頸,那就若干個(gè) Server 有相同的 MU,做輪詢。但總之我不推薦做磁盤 RAID,這可能是我還沒搞明白,也可能真的如此,就拿最簡(jiǎn)單的兩塊硬盤做 RAID 0 來說,按我的理解,每讀一個(gè)文件的時(shí)候,兩塊磁盤都需要定位,而兩塊獨(dú)立的磁盤卻可以分別各定位一個(gè)文件,據(jù)說也會(huì)受磁盤控制器的限制,但大體上不影響我的我的結(jié)論:RAID 的優(yōu)勢(shì)應(yīng)該在連續(xù)讀寫上,而隨機(jī)讀寫反而會(huì)因?yàn)槎贪逍?yīng)而略微降低 IOPS??傊@問題我只能紙上談兵,有條件應(yīng)該拿幾塊硬盤測(cè)一下的。
此外,諸如目錄分級(jí)是 1000 還是 100 個(gè)文件一組,還是用其他進(jìn)制,都可以細(xì)測(cè)。記得以前看過一個(gè)單目錄文件數(shù)的 Benchmark,可惜后來再也找不到了(有誰能提示一下?),隱約記得 500 比較合適,因?yàn)檫@里需要頻繁手工搬動(dòng),索性就弄個(gè)人腦容易識(shí)別的數(shù)了
另外為了防止遍歷需要加個(gè)擾碼,這也很容易了。
至于縮略圖接口,另算
應(yīng)用特例:頭像
其實(shí)每個(gè)網(wǎng)站都有的圖片機(jī)制是頭像,現(xiàn)在普遍的規(guī)則是用戶 ID + 補(bǔ)碼,補(bǔ)碼可以是更新次數(shù)(如 douban)、時(shí)間戳(如 t.sina)、文件名(如 twitter)之類的。補(bǔ)碼的主要目的是為了靠改變 URL 而繞開 Header Expires
但直到上家公司的產(chǎn)品部門在全面模仿 Facebook 運(yùn)動(dòng)中,想要實(shí)現(xiàn) Facebook 里的一個(gè)功能:所有圖片的大一統(tǒng),如新上傳的圖片進(jìn)相冊(cè)、可以將任意一張圖片設(shè)為頭像,而收藏功能的流程跟自己上傳幾乎是一樣的,等等,雖然這個(gè)改動(dòng)過于底層以至于辭職一年多后公司徹底黃掉也沒能看到它實(shí)現(xiàn),但我還是很喜歡這個(gè)大一統(tǒng)的:不去考慮一張圖片被哪些地方引用,只管存就是了,而所有涉及到圖片的地方,都保存的是圖片 ID,因此只看某一張圖片的 URL,你不知道頭像的主人是誰:有可能其他人也用相同的圖片做頭像,或者在某人的相冊(cè)里。
問題
再次強(qiáng)調(diào),這僅僅是個(gè)例子,針對(duì)每個(gè)網(wǎng)站、每個(gè)應(yīng)用的需求,方案也是千變?nèi)f化的。大家都有各自關(guān)注的重點(diǎn)。
所有圖片都是不刪的,刪的只是針對(duì)圖片的引用頁而已,也就是說,如果一個(gè)人傳了幾張私人圖片、由自己刪除后,如果別人知道圖片 URL,則還是可以訪問的(或者更簡(jiǎn)單的情況,某個(gè)被設(shè)置為限少數(shù)人訪問的相冊(cè)的圖片 URL 被公開),我不清楚實(shí)際運(yùn)營(yíng)時(shí)這個(gè)問題是否會(huì)很嚴(yán)重,總之不好回避。
對(duì) MU 的調(diào)配是基于人工的,而不是任何高科技的自維護(hù)系統(tǒng)。老實(shí)說我認(rèn)為就目前(2010 年)來說,除了 SSD 在某些特殊情況下能值回票價(jià),其他的一些高科技玩意真的看不懂,我目前還很排斥云××類東西,不僅僅是貴,最主要的原因是,即使錢花到了,它們做的還沒說的那么好。一個(gè)外行可以把技術(shù)視為魔法和銀彈,但作為從業(yè)者,基本的問題是應(yīng)該知道的,云存儲(chǔ)再牛逼,他也是跑在機(jī)械硬盤上的,而我們現(xiàn)有的工作,有很重要的一部分是在約定的系數(shù)下,加減成本和可靠性兩個(gè)指標(biāo),使其乘積最大。如果磁盤數(shù)量在可控范圍內(nèi),偶爾找人花幾個(gè)工時(shí)維護(hù)一下就夠,我相信普通的熱備就已經(jīng)是好的選擇。除非容量已經(jīng)上 PB 了,或者到了無緣無故的程度,可反過來說,那些魔法軟件又有多少 PB 級(jí)案例?總之云××可以等等,第一批沖上去的大都是炮灰。現(xiàn)階段我還是覺得每GB平均半毛錢不到的硬盤真他媽便宜好用
來源:http://soulogic.com/blog/archives/407.html
- 目前還沒評(píng)論,等你發(fā)揮!