先声明,本文借鉴了很多刘涛(Tarkus)和 Druggo Yang 的实战经验,特此感谢
好像现在是个网站就允许用户上传头像,其中一部分还允许上传相册、个性背景图之类的东西。对图片的规划各村都有各村的高招,这里只是抛砖引玉、提个醒:当文件膨胀到一定规模的时候再去改就来不及了,在一个项目的草创时期,让一个人多花两个星期的时间来琢磨这个“小”问题也绝对称不上是过度设计。
我对中等的定义:图片所占空间在 1T – 数十 T 之间。
功能需求
基本的就两点:排除重复,和可扩展性。
排重并不为很多人所重视,因为对很多人来说短期可以承受,实际经验重复的占了 50%(一些流行的图片会被重复上传很多次),但问题是这里还关系到另外一件事:审核。例如一些很流行的黄图会被频繁的被不同人重复上传,这也是不小的审核工作量。
具体设计
简单的说就是 MysqL 单点来保证唯一,将文件 MD5 转换为递增ID,再将固定数量的文件分成组,例如最简单的 MysqL 表可以这样。
`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
文件就是整个 ID 的补零切三段,如 12345 保存为实际的文件 /upload/000/012/345,不保留扩展名,统一发送 Header Content-Type application/octet-stream
首要原则是只新增,不改写(除了审核后删除的)
调优
上面所述只为了方便理解的最精简的方案,还有很多事情可以做。
用于发号的 MysqL 仅仅是写入单点,对于已经生成的 hash,可以定期生成 Archive 引擎 的只读表。然后新上传时以 SELECT/INSERT/SELECT 的顺序获取 ID(这时候,第三步的 SELECT 是极其罕见的)。
如果是 nfs 挂载,初期可以挂整个 /upload,后期可以以一级子目录为单位,分散在不同机器上。如 /upload/001 和 /upload/002 挂在机器 A 上,诸如此类。这样我们可以每 M 个图片为一个挂载单位(下面简称 MU——Mounted Unit)。如果图片的平均大小是 200K 左右,则每个 MU 的平均大小应该在 1M * 200K = 200G 左右。由于内容不再改写,老的 MU 可以到处搬动、甚至分散到不同机房,其实是类似 flickr 的 farm*.static 子域名。
实际磁盘问题是个大问题,这方面网友 Druggo Yang 给了我很多经验,他最早面临的问题是大量随机读写、几乎没热点,大概是文件实在太碎,硬盘反倒在网卡之前成为瓶颈,银弹是有:SSD,不过太tmd贵了。 最后他们是用的 LVS,那已经是我知识体系以外的东西了。
我想到的解决方法被 Druggo Yang 评价为自己做软 RAID:不同 MU 挂到不同盘,用 Nginx 访问,或者系统本身瓶颈,那就若干个 Server 有相同的 MU,做轮询。但总之我不推荐做磁盘 RAID,这可能是我还没搞明白,也可能真的如此,就拿最简单的两块硬盘做 RAID 0 来说,按我的理解,每读一个文件的时候,两块磁盘都需要定位,而两块独立的磁盘却可以分别各定位一个文件,据说也会受磁盘控制器的限制,但大体上不影响我 的我的结论:RAID 的优势应该在连续读写上,而随机读写反而会因为短板效应而略微降低 IOPS。总之这问题我只能纸上谈兵,有条件应该拿几块硬盘测一下的。
此外,诸如目录分级是 1000 还是 100 个文件一组,还是用其他进制,都可以细测。记得以前看过一个单目录文件数的 Benchmark,可惜后来再也找不到了(有谁能提示一下?),隐约记得 500 比较合适,因为这里需要频繁手工搬动,索性就弄个人脑容易识别的数了
另外为了防止遍历需要加个扰码,这也很容易了。
至于缩略图接口,另算
应用特例:头像
其实每个网站都有的图片机制是头像,现在普遍的规则是用户 ID + 补码,补码可以是更新次数(如 douban)、时间戳(如 t.sina)、文件名(如 twitter)之类的。补码的主要目的是为了靠改变 URL 而绕开 Header Expires
但直到上家公司的产品部门在全面模仿 Facebook 运动中,想要实现 Facebook 里的一个功能:所有图片的大一统,如新上传的图片进相册、可以将任意一张图片设为头像,而收藏功能的流程跟自己上传几乎是一样的,等等,虽然这个改动过于 底层以至于辞职一年多后公司彻底黄掉也没能看到它实现,但我还是很喜欢这个大一统的:不去考虑一张图片被哪些地方引用,只管存就是了,而所有涉及到图片的 地方,都保存的是图片 ID,因此只看某一张图片的 URL,你不知道头像的主人是谁:有可能其他人也用相同的图片做头像,或者在某人的相册里。
问题
再次强调,这仅仅是个例子,针对每个网站、每个应用的需求,方案也是千变万化的。大家都有各自关注的重点。
所有图片都是不删的,删的只是针对图片的引用页而已,也就是说,如果一个人传了几张私人图片、由自己删除后,如果别人知道图片 URL,则还是可以访问的(或者更简单的情况,某个被设置为限少数人访问的相册的图片 URL 被公开),我不清楚实际运营时这个问题是否会很严重,总之不好回避。
对 MU 的调配是基于人工的,而不是任何高科技的自维护系统。老实说我认为就目前(2010 年)来说,除了 SSD 在某些特殊情况下能值回票价,其他的一些高科技玩意真的看不懂,我目前还很排斥云××类东西,不仅仅是贵,最主要的原因是,即使钱花到了,它们做的还没说 的那么好。一个外行可以把技术视为魔法和银弹,但作为从业者,基本的问题是应该知道的,云存储再牛逼,他也是跑在机械硬盘上的,而我们现有的工作,有很重 要的一部分是在约定的系数下,加减成本和可靠性两个指标,使其乘积最大。如果磁盘数量在可控范围内,偶尔找人花几个工时维护一下就够,我相信普通的热备就 已经是好的选择。除非容量已经上 PB 了,或者到了无缘无故的程度,可反过来说,那些魔法软件又有多少 PB 级案例?总之云××可以等等,第一批冲上去的大都是炮灰。现阶段我还是觉得每GB平均半毛钱不到的硬盘真他妈便宜好用
来源:http://soulogic.com/blog/archives/407.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。