主题:【半原创】Flickr 网站架构研究(1) -- 西电鲁丁
我想除了历史原因,Slave存在的理由还在于
memcached只是简单的key-value pair,不能执行sql语句,所以不可能把整个数据库都放到memcached里,即memcached不能替代slave的功能,而且没有slave,memcached就成了无源之水;
没有slave而把全部读压力都放到master是不可行的,如文章所述一般来说master最多有两个,超过了会有其他的问题;
所以对于大型网站来说,找到这个memcached和slave的平衡点很重要。
这个意见很正确,刚才我的理解是错误的。
才能相互启发,一些与邓兄的讨论结果,我找时间会加入原来的帖子。
在具体讨论Flickr的文件存储系统设计之前,我们这里先来简要的介绍一下海量文件系统设计时需要考虑的一些基本因素:
1。数据库还是文件系统?
文件存在哪儿?这个可能是大家问的第一个也可能是最多的问题了,一般来说,业界公认的方法是数据库存放元数据和文件路径(可以是绝对路径,也可以是相对路径+卷路径),而实际文件则存放在文件系统,大多数大型图片网站如Flickr和企业内容管理(ECM)软件厂商都采用这种方法(或者同时支持数据库和文件系统两种方式),原因在于
1)数据库系统在增大到一定规模后,比如上TB级后管理维护和效率都是一个很大的挑战(升级硬件是一个办法,如果有钱),例如Flickr现今已有超过6PB的照片文件,全部以BLOB方式存入数据库几乎不可能。当然也有例外,比如微软的Sharepoint 2007就是全部文件都存入SQL SERVER数据库的,微软的建议是一个Sharepoint library的文件数不要超过5百万,如果超过的话则可以采用scale-out方式。记得几年前在MSDN上看过一篇文章,介绍微软采用了几百台sharepoint 2001 server来搭建自己的一个内部知识库网站。采用数据库的方式,虽然有些限制,但也不是绝对不可以。
2)以BLOB方式存储文件,当文件大小超过一定阀值后数据库的读写响应将急剧下降。下面的图摘自某著名ECM厂商的研究报告,这张是比较不同大小的文件存入数据库和文件系统的响应时间的(蓝线是数据库,红线是文件系统)
这张是比较从数据库和文件系统读取不同大小的文件的响应时间的
从中可以看出,这个阀值是大约500KB-1MB左右,小于这个阀值,数据库的读写效率要高于文件系统,而大于这个阀值,文件系统的优势就体现出来了。不同的测试条件和软硬件下这个阀值会有所不同,但趋势应该是一致的。
采用文件系统方式的一个主要缺点是难以保证元数据和文件的一致性(在数据库方式下可以通过事务的方式来保证这一点),所以需要定期运行特定的程序进行一致性检查。
2。通用文件系统的问题。
早期的通用文件系统的设计初始并没有考虑到海量文件的存取,主要的表现为当一个目录下的文件数超过一定时,访问这个目录下文件的时间将大大增加,例如在NT4.0时代,这个值是大约5000,后来的Windows 2000/2003和Unix/Linux要好的多,不过这个限制依然存在。早期的NTFS或基于BSD Fast Filesystem的Unix/Linux文件系统,对于目录/inode的设计是采用采用简单的顺序查找的方式来定位目录下的文件,虽然稍后一些的文件系统设计增加了cache来提高效率,不过还是难以应付一个目录下有大量文件的情况;较为现代的JFS, Reiserfs, XFS, HFS和较新的NTFS版本对于目录结构采用了B-Tree的设计,因而大大提高了效率,但存在的问题是一旦较高层次的父节点数据损害,则可能会丢失所有的子节点。Linux 2.6的ext3和ext4文件系统有鉴于此,采用了一种相对简单但较高效和安全的HTree(Hash Tree)的方法,即可以利用hash table来提高访问效率,又保留了原来的顺序查找的结构,这样一旦hash table的数据丢失也依然可以通过顺序查找的结构来访问和恢复。
尽管效率有所提高,还是不建议在一个目录和一个文件系统下放置太多的文件。对于NTFS下的目录,个人理解这个临界点大约是30万,微软建议,当一个NTFS目录下文件数超过30万时,关掉短文件名(即DOS兼容模式的8.3)的生成(可通过注册表或fsutil behavior set disable8dot3 1命令)。对于Linux/Unix的目录以及单个文件系统,没有看到过权威的数据。(个人建议不要超过2级目录,总文件数不超过1亿吧。)一个现实的例子就是Youtube.com,Youtube最初采用几台Linux服务器存放所有视频的截屏(一个视频大约4-5个截屏),后来由于目录下文件太多以至大大影响了性能,在被Google收购之后,移植到了Google的BigTable,才最终解决了问题。
对于类似的照片网站,另一个建议是关掉文件系统的last access time的读写。缺省情况下,每一次文件访问,文件系统都会更新文件的last access time,这样会造成大量的写操作。对于这类网站,其实并不关心每一个文件的最后访问时间。对于Unix/Linux,可以通过在mount时选用noatime参数来关掉last access time;对于NTFS,可通过注册表或fsutil behavior set disablelastaccess 1的命令。但要注意的是,一些备份软件/策略可能根据这个时间来进行备份,在关掉这个选项之前需要仔细考虑替代方案。
除了通用文件系统之外,也有一些基于通用文件系统加以改造或是干脆重起炉灶重写的分布式文件系统比如Google File system(GFS),IBM 的General Parallel File System (GPFS),和著名的memcached师出同门的MogileFS,以及Facebook新近研发成功的Haystack系统等,这些系统在一定程度上克服了通用文件系统的限制,但在成熟度,开放性和技术支持上还有待观察。
3。存储设备的选择。
存储设备的选择是个见仁见智的事情,大厂商的存储设备通常有一些独特的功能,比如snapshot,replication,WORM-Like Disk(类似CD-R以满足法律法规的要求)等,缺点当然就是价格昂贵;而通用硬件价格便宜,采购灵活,缺点是应用软件需要实现更多的功能,而且维护成本较高,可能需要几百台服务器来达到同样的存储容量。对比两大网站:Flickr最初采用了通用的硬件而后似乎是基于维护的原因而转向NetApp;而Facebook在砸出大把大把的银子购进NetApp后最近由于性能和TCO的原因开始考虑通用硬件。重要的在于软件和应用系统的设计要不依赖于特定厂商的产品和功能,这样在需要的时候可以有更多的选择,虽然数据移植是一件很痛苦的事。
写得真好。吹捧的话不多说了,直接开始拍砖,
YouTube是把视频存储在BigTable里吗?BigTable是数据库,而不是文件系统。Google的文件系统是GFS。按前面的逻辑讲,似乎YouTube的视频文件应该存放在GFS,而把元数据以及文件路径放在BigTable数据库里。这样的理解是否正确?
另外,
1. 有没有关于YouTube的架构设计的文章?
2. 有没有关于Twitter架构设计的文章?把Twitter的架构与Flickr的比较一下,应该很有启发。因为这是两类不同性质的大型网站。Flickr是一站式,而Twitter本质上是离散的。
3. 翻阅了一下“Building Scalable Web Sites”,该书作者就是Flickr的架构师,Cal Henderson。但是感觉不是很好,内容庞杂,而没有突出Large scale的挑战与解决办法。
但是发现[URL=http://www.iamcal.com/talks/
]Cal Henderson的个人网站[/URL]上有很多内容。那几篇文章内容比较翔实,可否推荐一下,共同阅读,相互切磋,共同提高。
多谢好文!
1. YouTube是把"视频的截屏",thumbnails存储在BigTable里,视频本身应该还是在文件系统里。关于GFS和bigtable本身,大概看过一点(暂时没有时间细看),GFS适合于100MB以上的大文件,而bigtable是基于GFS之上的。关于youtube的架构,参见http://highscalability.com/youtube-architecture,YouTube的视频文件的元数据可能还是在MYSQL数据库.
2。上面这个网站上也有关于twitter的文章 http://highscalability.com/scaling-twitter-making-twitter-10000-percent-faster
这个网站和西西河一样,是我每天必看的。
3. Cal Henderson这本书精华在第9章,不过其他章节也不要错过,一些细节很分散,我先后看了N遍。有些内容比较含糊,要和他个人网站上的文章和其他人的文章结合着看。我几乎把他所有的文章都看过了,如果注重scalability的话,可以重点看下面这两篇
16/09/2008 Web 2.0 Expo NYC Scalable Web Architectures: Common Patterns and Approaches
16/04/2007 Web 2.0 Expo, San Francisco Beyond the File System: Designing Large-Scale File Storage and Serving
Flickr在美国东西海岸各建了4个数据中心用于提供静态内容(主要是照片)的后台访问服务,组成了4个相互镜像和冗余的“Photo Farm Pair",各自服务于不同地域的客户。同Flickr"Master-Maste Pair"数据库一样,每个数据中心的设计容量是能够处理整个"Pair"100%的访问,这样在"Pair"中的一个数据中心因为停电,地震等灾难或必要维护而停止服务时,另一个数据中心仍然能够支持全部的访问而不降低性能。
每个"Photo Farm"的架构示意图如下:
这个就是所谓的"Flickr File System",主要由三部分组成:1)Perbal(未在图中标出)和PHP APP Servers提供文件访问的权限控制,获取文件的内部URL并转向Squid/Apache服务器;2)Layer 7 Load Balancer和多台Squid/Apache Servers(Apache未在图中标出)提供文件“读”访问服务;3)几台Storage Managers用于文件“写”访问服务,PHP APP Server和Storage Manager之间采用了Flickr自己开发的“文件传输轻量协议“。NetApp用于文件存储,Squid/Apache Server和Storage Manager Server和NetApp之间采用NFS连接。
我们先来看访问控制这一部分。
Flickr的访问权限控制是通过Perlbal(Perlbal是一个Perl 编写的单线程的事件驱动服务器,可充当 reverse proxy,Web 服务器 和 HTTP Balancer,与大名鼎鼎的memcache同出于www.danga.com)的"reproxy"功能来实现的,如下图:
例如,当Perlbal收到以下URL访问请求时,”http://www.flickr.com/photos/41174610@N05/3999568691/“,首先将访问请求转向后台的PHP App Server 进行权限检查,如果当前浏览器用户具有访问权限,则向Perlbal返回HTTP Response 200,并设置"X-REPROXY-URL" Header为实际的静态URL地址,既”http://farm3.static.flickr.com/2434/3999568691_d836afbb57.jpg“,Perlbal收到X-REPROXY-URL后,将请求转向Asset Server既Squid/Apache Server进行处理,整个过程对用户透明。
以上例子中我们看到Flickr采用了两种不同方式的URL格式,第一种方式(例如”http://www.flickr.com/photos/41174610@N05/3999568691/“)我们姑且称之为“动态URL",当最终用户采用这种方式访问时,后台系统将进行权限检查;而如果用户采用第二种方式(例如”http://farm3.static.flickr.com/2434/3999568691_d836afbb57.jpg“),我们姑且称之为“静态URL",系统将略过权限检查,直接指向Squid/Apache服务器。这就是Flickr所谓“Permission URLs”的概念,即将授权信息作为URL的一部分,从而省略授权过程而提高网站的访问性能和吞吐量。
个人认为,Flickr是鼓励采用其Flickr API开发第三方接口的合作伙伴采用这种“静态URL"方式的。Flickr的API文档中提供了这种静态URL的信息格式
”http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}.jpg“
or
”http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_[mstb].jpg“
or
”http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{o-secret}_o.(jpg|gif|png)“
Size Suffixes
The letter suffixes are as follows:
s small square 75x75
t thumbnail, 100 on longest side
m small, 240 on longest side
- medium, 500 on longest side
b large, 1024 on longest side (only exists for very large original images)
o original image, either a jpg, gif or png, depending on source format
“静态URL"也可以通过“动态URL”来获得,而且是“终生授权”,即使将来作者修改了照片的权限,采用静态URL依然可以继续访问,除非作者删除照片,或者对照片进行修改,一旦照片经过修改,则系统自动生成一个新的“静态URL",原有的授权则因URL不存在而自动失效。
对于Flickr这类以"照片共享"为主要目的的社交类网站,安全并不是其首要的考虑因素,因而采用这种“折衷”方式的权限控制方式是可以接受的。(听说Amazon S3也是采用了这种方式,不禁为那些S3商业用户的数据安全悄悄捏了把汗)
对于Flickr这样每秒需要响应和处理4万个以上的照片文件读取请求的大型网站系统,Web Cache和reverse proxy系统的重要性,无论怎么强调都不过分。常见的Web Cache系统有,Squid,Varnish,Apache mod_cache等。
Flickr采用Squid作为缓存代理服务器,这里有一张2005年Flickr展示的系统配置原理示意图:
简单解释一下,Squid采用2GB 内存作为一级缓存(注这个值仅仅是Squid用于缓存的内存大小,Squid实际所用的内存要大于这个值);服务器内置的6块15K RPM 高速硬盘作为二级缓存;小于64KB的文件缓存于内存,大于64KB而小于512KB的缓存于高速硬盘;当缓存空间不够时采用LRU(Least recently used)算法将”最近最少使用的文件“清除。
对于不在Squid缓存的文件,Squid则将请求转向Apache Web Server,Apache 根据ReWrite规则将URL转化成实际的NFS路径并取得相应的文件返回(图中只列出了一个Filer卷,实际上每个Squid服务器通过NFS mount了多个NetApp Filer的所有的卷)。值得一提的是,Apache在处理大于10MB以上的文件时,性能会有显著的下降,这也是为什么YouTube采用lighttpd的原因,所幸的是,一般照片的尺寸远远小于10MB,因而Flickr并不需要考虑额外维护另外一个Web Server,Apache已经足够满足需要。
Web缓存代理服务器常见的两个问题是:
1)如何避免”多次缓存“,即我们希望同一份文件只缓存一次,而不是在多个Squid中保留多份COPY,以最大限度的利用缓存空间。解决这个问题的办法是在Squid服务器集群之前放置Layer 7的Load Balancer。相对于传统的基于IP地址加PORT端口的Layer 4的Load Balancer,Layer 7的Load Balancer可以根据规则,对应用层的信息,如HTTP的Header,URL,Cookie Name等进行哈希或CRC计算,从而确保同一URL的请求总是指向后台集群中的同一台Squid服务器。
根据需要和负载,网站可以选择基于硬件的Layer 7 Load Balancer,如Flickr;也可以选择开源的软件方案,如具有Layer 7负载均衡功能的HAProxy,Perlbal,或者是利用Apache的mod_rewrite功能通过script来简易实现,示例代码如下:
RewriteEngine on
RewriteMap balance prg:/var/balance.pl
RewriteLock /var/balance.lock
RewriteRule ^/(.*)$ ${balance:$1} [P,L]
每一个到达的URL请求将被转向到/var/balance.pl脚本,由balance.pl脚本决定最终由哪台Cache服务器提供服务。
#!/usr/bin/perl -w
use strict;
use String::CRC;
$|++;
my @servers = qw(cache1 cache2 cache3);
while (<STDIN>) {
my $crc = String::CRC::crc($_, 16);
my $server = $servers[$crc % scalar @servers];
print "http://$server/$_";
}
(以上脚本省略了fail-over和后台服务器状态检查部分)
2)如何处理缓存”过期”问题,即当后台文件(如照片)修改后,如何保证用户能够访问到最新的版本而不是过期的版本。简单的更新Squid缓存并不总能解决问题,过期的文件依然有可能缓存在客户端的浏览器Cache,或是互联网上CDN,Proxy Server等处,而且还增加了后台应用的处理复杂程度和系统负荷。
Flickr的方案是引入了“虚拟版本”的概念(对于Flickr这样的应用,没有必要采用真正的版本管理和维护多个版本),让我们来回顾一下Flickr "静态URL"的格式:“http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}.jpg”,
每当用户修改照片时,数据库将重新生成一个新的的版本号码,即{secret}号码,从而产生一个新的静态URL并返回给客户,原有的缓存中的老文件则随着时间而“自然死亡”。(为了节省I/O,真正的物理文件名是没有这个{secret}号码的,在Apache的ReWrite处理中将这一部分去掉了。)这种方案,应该说至少可以保证通过“动态URL”访问的客户总是取得最新的文件版本,通过Flickr API访问的用户,则总是可以通过相应的应用逻辑判断来获取最新的“静态URL"。至于那些的Bookmark在浏览器端的旧的“静态URL",看起来也只能听之任之了,好象也没有更好的办法。
Flickr的缓存系统是相当高效的,这里有一些2007年的数据:
这部分看起来有一些浪费?现在是否可以使用云来替代?还是云在某些方面满足不了flickr的要求?
原来flickr是用这样的策略做负载均衡的,有意思,这样确实能保障squid缓存的内容不同,后面的图表也能看到缓存命中率不错,但是这样做从另一个角度看人为增加了群集节点的差异性,在failover和管理方面会比较麻烦,例如,如果负责A内容的squid节点当掉,在添加进新的节点或者转移请求到其他节点之前,A内容缓存命中率为0,加入新节点或者转移到其他节点之后,缓存命中率也有一个上升的过程,也就是说,有降低整体性能的可能。或者说,在flickr的负载平衡策略里面还有什么花样?让squid节点缓存内容适度形成交集?如果是,那么交集的决定策略又是什么?
做个大站,费神的事情真不少。。。
1. 50%的说法确实不太准确,已改为“每个数据中心的设计容量是能够处理整个"Pair"100%的访问”。事实上由于东西海岸的数据中心服务于不同地域的客户,时差不同,不会同时处于峰值,这样系统的冗余应该没有50%这么大,考虑到突发事件和“灾备”的作用,这部分冗余应该是可以接受的。
一个小故事是:2005年7月,Yahoo收购Flickr后刚刚把Flickr网站从温哥华移到德州的Yahoo数据中心,在修改DNS指向新的数据中心后才一小时,伦敦地铁发生爆炸案,当地人将不少照片上载到Flickr,随后是大量的互联网报道链接指向这些照片,Flickr的访问量激增,远超以前的峰值,如果不是新数据中心有足够的容量,Flickr恐怕就"挂"掉了。
云计算,看起来很美,实际还有很多细节没有解决,比如很多云服务提供商的SLA条款不够完善,对于租用者来说很难量化并监督以保证对最终用户的服务质量;由于对于租用者来说是个”黑盒子“,所以很难把握系统的实际性能及运行情况,给系统监控和容量规划带来困难;应用架构可能需要调整以适应云计算等等。另外,从费用来讲,云计算并不一定便宜,实际上不同企业情况不同,不能一概而论。
2。关于Squid的问题,其实可以细分为以下几个问题
1)Squid是否支持复制功能(replication)?
这个我不是Squid专家,好象没看到,看到的功能都是如何防止duplicate.道理上讲在Layer 7的Load Balancer的规则上做点手脚,比如哈希后”模2" 等,让Load Balancer将特定的URL指向两台Squid服务器中的任意一台,好象是可以实现的。
2)一台Squid失效,对Farm或者整个网站有多大的影响?根据2007年的数据,Flickr每秒处理4万个文件访问请求,而图中一台Squid的每秒峰值是620,平均值不到500个,粗略计算得知,Flickr应该有大约80台Squid服务器,每个数据中心大约10台左右,那么损失一台总体上对于一个数据中心的影响仅10%,对整个网站则几乎可以忽略不计了。
3)一台Squid需要多少时间Warm up? 不知道。 哪位河友能提供一下数据?
实际上没有什么太大花头。我用berkeleydb做过一个类似的server,每秒每台机器的平均请求在600左右。每次请求数据传输在1k到10k之间--当然比flickr小多了。但是监控性能下来,目前只达到系统负载的25%左右。一台机器的数据总量在200到300GB,具体多少我忘记了。平均相应时间在10ms以下。当然,这是我server内部的数据,对用户的网络接口在我server的前面。仅仅反应的是berkeleydb所能处理的吞吐量而已。
话说回来,squid所作的cache基本上都依赖于berkeleydb来做,他做的事情是根据http协议做的一些优化(理解可能有误)。如果自己需要做某些简单的cache,不如用berkeleydb更来得合适。实际上,我觉得bdb还不太合适。B+tree做cache有些overkill,如果没有什么频繁的数据更新,用lucene效率更高。要不是现在我们的产品负载还可以,我是想把数据移入lucene的。
刚才看了一遍,没有发现什么疑问。
通常情况,如果没有疑问,多半说明没看明白,还得反复看。
估计是办公室太热了,头脑昏昏。看这些文章,得找个凉风习习的去处。。
回头再读,再读,直到读出问题来。
没看过berkeleydb的资料,不好评论,看起来主要的结构是key/data pair,应该没有太多的overhead.
Flickr自己说Squid在overload情况下会占用很多cpu和network资源,达到100%,他们也考察过varnish,据说在一些情况下效率更高,不过好象还不太稳定。另外Facebook用的是memcache做文件缓存,我在下一篇会提到。
关于lucene,这不是搜索引擎吗?怎么反而效率比专门的缓存服务器还高?
没有实际试过,事实上,lucene代替squid都没有想过,值得好好琢磨一下。
楼主好文, 解释的相当清楚。
有时间是不是再讲讲当前最流行的facebook一类的sns网站的架构?
感觉这类sns网站因为数据之间的相关联系复杂的多,设计架构可能
难度更大。
flickr做的cache实际上是不适合用lucene的,因为更新太多了,所以需要用update更高效的B+ tree。
而我们的应用,很少更新,仅仅是纯cache。lucene虽说是搜索引擎toolkit,但是数据结构是用inverted index,在IO方面,做了很大的优化。在纯query操作,速度明显高于B+tree。而大数据量的cache,IO是必须优化的。B+tree在数据量到一定程度,index比数据本身还大的多,而且不容易压缩。而inverted index长处在于数据结构简单,可作的优化非常多,而lucene也做了这么多优化的。
具体的,我没空找相关文章了。
整体来讲,squid是一个通用的cache system,未必适合特定的应用,或者说特定的应用可能有更好的做cache的办法的。