星期日, 十月 16, 2011

各大网站架构总结笔记

记得在大学里不止一次关注网站架构方面的东西了,但每次都是泛泛了解,也没有着重记录,一段时间后对各种架构的思想也就模糊了。这几天不知怎么的又心血来潮(可能是快毕业了冲动了)想深入了解一下网站架构方面的知识,并想通过这次来总结一下网站架构,记录一点东西供自己以后翻阅,也给那些希望了解这方面知识的朋友提供一点点有用的信息,下面是我这次学习的总结笔记,有什么写得不妥的地方还请大家指出,还有希望这篇随笔能抛砖引玉,大家各抒己见。

1、MySpace架构

回顾了MySpace的成长史,真是让人惊叹,他的架构基本经历了五个里程碑,每个阶段都是显得那么仓促,那么无奈,那么坎坷,又是那么的精彩,网站为了生存只能想尽一切办法去优化系统架构,让用户满意。他给我们后人的启示是要尽早发现系统的瓶颈,设计师在设计时要有前瞻思想,否则今后有可能也要这样仓促的升级你的产品。
这里是“五个里程碑”的具体介绍。

回顾MySpace架构的坎坷之路

说起MySpace,可能很多人对他印象很深,MySpace.com成立于2003年9月,是目前全球最大的社交网站。它为全球用户提供了一个集交友、个人信息分享、即时通讯等多种功能于一体的互动平台,同时他也是.NET应用最出色的网站之一。下面我们一起来回顾一下MySpace架构的改革之路,或许我们能从中得到一点点架构方面的经验和教训。

1、50万用户

最早myspace网站由一台数据库服务器和两台Web服务器构成,此后一段时间又加了几台web服务器。但在2004年早期,用户增加到50万时一台数据库服务器就显得力不从心了。

他们设计了第一代架构,在此架构中他们运行3个SQL Server2000服务器,一个为主,所有的新数据都提交给他,然后再复制给其他两个数据库服务器。另外两台服务器用来给用户提供信息浏览,也就是只做数据读取。在一段时间内效果不错,只需要增加数据库服务器,扩大硬盘,就可以应对用户数和访问量的增加了。

2、100万-200万用户

当达到这个数字时,myspace数据库服务器遇到了I/O瓶颈,即他们存取数据的速度跟不上了。而这时据他们第一个架构只要5个月。有人花5分钟都无法完成留言,很多用户认为myspace完蛋了。

这个时候新的架构被快速提出来了,这一次他们把数据库架构按照分割模式设计,以网站功能分出多种,如登陆、现实用户资料、博客信息、等分门别类存储在不同的数据库服务器里。这种垂直分割策略利于多个数据库分担访问压力(天涯曾经就这么做过)。后来myspace从存储设备与数据库服务器直接交互的方式SAN(用高带宽和专门设计的网络将大量磁盘存储设备链接在一起,而数据库链接到SAN)。

3、300万用户

到300万用户时,这种架构开始也不行了,因为每个数据库都必须有每个用户表副本,意识是一个用户注册后,他的信息会分别存在每个数据库中,但这种做法有可能某台数据库服务器挂掉了,用户使用一些服务可能会有问题。另一个问题是比如博客信息增长太快,专门为他服务的数据库的压力过大,而其他一些功能很少被使用又在闲置。这就好像有人忙的要死,有人闲的要死。

于是他们购买了更好更贵的服务器来解决管理更大数据库的问题。但专家预测他们即使昂贵专业的服务器到最后也会不堪重负,他们必须调整架构而不是掏钱买更好的服务器。于是他们的第三代架构出现了。分布式计算架构,他们分布众多服务器,但从逻辑上看成是一台服务器。拿数据库来说,不能再按功能拆分了,看成只有一个数据库服务器。数据库模型中维护一个用户表、博客信息表、等等同看作在一个数据库服务器中。

然后他们开始把用户按每百万一组分割,每一组的用户访问指定的数据库服务器。另外一个特殊服务器保存所有用户的帐号和密码。他们的设计师说如果按照这种模式以更小粒度划分架构是可以进一步优化负荷负担的(50万用户为一组 或者更少)。

4、900万-1700万用户

myspace在这个时候把网站代码全部改为.net语言,事实证明网站跑的比以前快了很多、执行用户的请求消耗非常少的资源,后来他们把所有的程序都改成.net了。但问题到1000万时还是出来了。

用户注册量太快,按每100万分割数据库的策略不是那么完美,比如他们的第7台数据库服务器上线仅仅7天就被塞满了。主要原因是佛罗里达一个乐队的歌迷疯狂注册。而且某台数据库服务器可以在任何原因 任何时候遭遇特别大的负荷。他们的解决办法是人工把崩溃的数据库里的用户迁移走。但这不是一个好办法。

这个时候myspace购买了3PAdata设备,他的牛逼之处是真正把所有的数据库看成一个整体。他会根据情况把负荷平均分配出去,比如当用户提交一个信息,他会看哪个数据区域空闲然后分配给他,然后会在其他多处地方留有副本,不会出现一台数据库服务器崩溃,而这台数据库里的信息没有办法读取的情况,这样做看起来好极了。

另外他们增加了缓存层,以前用户查询一个信息,就请求一次数据库,现在当一个用户请求数据库后,缓存层就会保留下来一个副本,当其他用户再访问时就不需要再请求数据库了,直接请求缓存就够了。

5、2600万用户

他们把服务器更换到运行64位的服务器,这样服务器上可最多挂上32G内存,这无疑有提升了网站性能,用户感觉这个网站开始稳定快起来了。但一个新问题意外出现了。他们放数据库服务中心的洛杉矶全市停电了。这导致整个系统停止运行长达12个小时。

这时他们实现了在地理上分布多个数据中心以防止洛杉矶事件再次出现,在几个重要城市的数据中心的部署可以防止某一处出现故障,整个系统照样提供服务,如果几个地方都出现故障,那么这就意味着国家出现了重大灾难,这种几率是非常低的。

6、总结

这个架构变化升级相当有意思,架构随着用户量的提升作仓促的变化,但又恰到好处,看来MySpace又验证了一句古话“有压力才会有动力”。同时他给我们后人的启示是要尽早发现系统的瓶颈,设计师在设计时要有前瞻思想,否则今后有可能也要这样仓促的升级你的产品。

2、Flickr网站架构总结

Flickr.com 是网上最受欢迎的照片共享网站之一,还记得那位给Windows Vista拍摄壁纸的Hamad Darwish吗?他就是将照片上传到Flickr,后而被微软看中成为Vista壁纸御用摄影师。

--Pair of ServerIron's做负载均衡

--Squid做html和照片的缓存

--Memcached做数据缓存

--尤其是mysql数据库采用master-slave和shards技术实现了mysql数据库的负载均衡,解决了数据库的瓶颈,达到了数据库横向扩展的目标。

这里是他详细的架构描述

Flickr 网站架构分析

Flickr.com 是网上最受欢迎的照片共享网站之一,还记得那位给Windows Vista拍摄壁纸的Hamad Darwish吗?他就是将照片上传到Flickr,后而被微软看中成为Vista壁纸御用摄影师。

Flickr.com 是最初由位于温哥华的Ludicorp公司开发设计并于2004年2月正式发布的,由于大量应用了WEB 2.0技术,注重用户体验,使得其迅速获得了大量的用户,2007年11月,Flickr迎来了第20亿张照片,一年后,这个数字就达到了30亿,并且还在以加速度增长。 2005年3月,雅虎公司以3千500万美元收购了Ludicorp公司和Flickr.com。虽然Flickr并不是最大的照片共享网站(Facebook以超过100亿张照片排名第一),但这笔收购仍然被认为是WEB 2.0浪潮中最精明的收购,因为仅仅一年后,Google就以16亿美元的高价收购了YouTube,而2007年10月,微软斥资2.4亿美元收购 Facebook 1.6%股份,此举使Facebook估值高达150亿美元。估计Ludicorp公司的创始人Stewart Butterfield和Caterina Fake夫妇现在还在后悔吧。

在2005年温哥华PHP协会的简报以及随后的一系列会议上,Flickr的架构师Cal Henderson公开了大部分Flickr所使用的后台技术,使得我们能有机会来分享和研究其在构建可扩展Web站点的经验。本文大部分资料来自互联网和自己的一点点心得,欢迎大家参与讨论,要是能够起到抛砖引玉的作用,本人将不胜荣幸。

Flickr整体框架图

在讨论Flickr 网站架构之前,让我们先来看一组统计数据(数据来源:April 2007 MySQL Conf and Expo和Flickr网站)

  1. 每天多达40亿次的查询请求
  2. squid总计约有3500万张照片(硬盘+内存)
  3. squid内存中约有200万张照片
  4. 总计有大约4亿7000万张照片,每张图片又生成不同尺寸大小的4-5份图片
  5. 每秒38,000次Memcached请求 (Memcached总共存储了1200万对象)
  6. 超过2 PB 存储,其中数据库12TB
  7. 每天新增图片超过 40万(周日峰值超过200万,约1.5TB)
  8. 超过8百50万注册用户
  9. 超过1千万的唯一标签(tags)
  10. 响应4万个照片访问请求
  11. 处理10万个缓存操作
  12. 运行13万个数据库查询
这张是Flickr的网站架构图,我们这里只作一些简要的描述,具体的分析请静待后续文章。

点看全图

  1. Pair of ServerIron's做负载均衡方案
  2. Squid Caches 代理,用于缓存静态的HTML和照片
  3. Net App公司的Filer, NAS存储设备,用于存储照片
  4. PHP App Servers
    - 运行REDHAT LINUX,Apache上的PHP应用,Flickr网站的主体是大约6万行PHP代码
    - 没有使用PHP session, 应用是stateless,便于扩展,并避免PHP Server故障所带来的Session失效。
    - 每个页面有大约27~35个查询(不知道为什么这么设计,个人觉得没有必要)
    - 另有专门的Apache Web Farm 服务于静态文件(HTML和照片)的访问
  5. Storage Manager 运行私有的,适用于海量文件存储的Flickr File System
  6. Dual Tree Central Database
    - MySQL 数据库,存放用户表,记录的信息是用户主键以及此用户对以的数据库Shard区,从中心用户表中查出用户数据所在位置,然后直接从目标Shard中取出数据。
    - “Dual Tree"架构是”Master-Master"和“Master-Slave"的有效结合,双Master 避免了“单点故障”,Master-Slave又提高了读取速度,因为用户表的操作90%以上是读。
  7. Master-master shards
    - MySQL 数据库,存储实际的用户数据和照片的元数据(Meta Data),每个Shard 大约40万个用户,120GB 数据。每个用户的所有数据存放在同一个shard中。
    - Shard中的每一个server的负载只是其可最大负载的50%,这样在需要的时候可以Online停掉一半的server进行升级或维护而不影响系统性能。
    - 为了避免跨Shard查询所带来的性能影响,一些数据有在不同的Shard有两份拷贝,比如用户对照片的评论,通过事务来保证其同步。
  8. Memcached Cluster 中间层缓存服务器,用于缓存数据库的SQL查询结果等。
  9. Big Search Engine
    - 复制部分Shard数据(Owner’s single tag)到Search Engine Farm以响应实时的全文检索。
    - 其他全文检索请求利用Yahoo的搜索引擎处理(Flickr是Yahoo旗下的公司F
  10. 服务器的硬件配置:
    - Intel或AMD 64位CPU,16GB RAM
    - 6-disk 15K RPM RAID-10.
    - 2U boxes.
  11. 服务器数量:(数据来源:April 2008 MySQL Conference & Expo)
    166 DB servers, 244 web servers(不知道是否包括 squid server?), 14 Memcached servers

数据库最初的扩展-Replication

也许有人不相信,不过Flickr确实是从一台服务器起步的,即Apache/PHP和MySQL是运行在同一台服务器上的,很快MySQL服务器就独立 了出来,成了双服务器架构。随着用户和访问量的快速增长,MySQL数据库开始承受越来越大的压力,成为应用瓶颈,导致网站应用响应速度变慢,MySQL 的扩展问题就摆在了Flickr的技术团队面前。
不幸的是,在当时,他们的选择并不多。一般来说,数据库的扩展无外是两条路,Scale-Up和Scale-Out,所谓Scale-Up,简单的说就是 在同一台机器内增加CPU,内存等硬件来增加数据库系统的处理能力,一般不需要修改应用程序;而Scale-Out,就是我们通常所说的数据库集群方式, 即通过增加运行数据库服务器的数量来提高系统整体的能力,而应用程序则一般需要进行相应的修改。在常见的商业数据库中,Oracle具有很强的 Scale-Up的能力,很早就能够支持几十个甚至数百个CPU,运行大型关键业务应用;而微软的SQL SERVER,早期受Wintel架构所限,以Scale-Out著称,但自从几年前突破了Wintel体系架构8路CPU的的限制,Scale-Up的 能力一路突飞猛进,最近更是发布了SQL 2008在Windows 2008 R2版运行256个CPU核心(core)的测试结果,开始挑战Oracle的高端市场。而MySQL,直到今年4月,在最终采纳了GOOGLE公司贡献 的SMP性能增强的代码后,发布了MySQL5.4后,才开始支持16路CPU的X86系统和64路CPU的CMT系统(基于Sun UltraSPARC 的系统)。
从另一方面来说,Scale-Up受软硬件体系的限制,不可能无限增加CPU和内存,相反Scale-Out却是可以"几乎"无限的扩展,以Google 为例,2006年Google一共有超过45万台服务器(谁能告诉我现在他们有多少?!);而且大型SMP服务器的价格远远超过普通的双路服务器,对于很 多刚刚起步或是业务增长很难预测的网站来说,不可能也没必要一次性投资购买大型的硬件设备,因而虽然Scale-Out会随着服务器数量的增多而带来管 理,部署和维护的成本急剧上升,但确是大多数大型网站当然也包括Flickr的唯一选择。
经过统计,Flickr的技术人员发现,查询即SELECT语句的数量要远远大于添加,更新和
删除的数量,比例达到了大约13:1甚至更多,所以他们采用了“Master-Slave”的复制模式,即所有的“写”操作都在发生在“Master", 然后”异步“复制到一台或多台“Slave"上,而所有的”读“操作都转到”Slave"上运行,这样随着“读”交易量的增加,只需增加Slave服务器 就可以了。
点看全图
让我们来看一下应用系统应该如何修改来适应这样的架构,除了”读/写“分离外,对于”读“操作最基本的要求是:1)应用程序能够在多个”Slave“上进 行负载均分;2)当一个或多个”slave"出现故障时,应用程序能自动尝试下一个“slave”,如果全部“Slave"失效,则返回错误。 Flickr曾经考虑过的方案是在Web应用和”Slave“群之间加入一个硬件或软件的”Load Balancer“,如下图
点看全图
这样的好处是应用所需的改动最小,因为对于应用来说,所有的读操作都是通过一个虚拟的Slave来进行,添加和删除“Slave"服务器对应用透 明,Load Balancer 实现对各个Slave服务器状态的监控并将出现故障的Slave从可用节点列表里删除,并可以实现一些复杂的负载分担策略,比如新买的服务器处理能力要高 过Slave群中其他的老机器,那么我们可以给这个机器多分配一些负载以最有效的利用资源。一个简单的利用Apache proxy_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
。。。。。。。。。。。。。。。。。。。。
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.
最终,Flickr采用了一种非常“轻量”但有效的“简易”PHP实现,基本的代码只有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函数,作用是将数组中每个元素的顺序随机打乱。
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;
}
在上述代码中,如果需要对特定的Slave赋予更高的负载,只要在$hosts中多出现一次或多次就可以了。这段代码只要稍稍改进,就可以实现更复杂的功能,如当connect失败时自动将host从hosts列表中去除等。
“Master”-"Slave"模式的缺点是它并没有对于“写'操作提供扩展能力,而且存在单点故障,即一旦Master故障,整个网站将丧失“更新” 的能力。解决的办法采用“Master"-"Master"模式,即两台服务器互为”Master“-"Slave",这样不仅”读/写“能力扩展了一 倍,而且有效避免了”单点故障“,结合已有的“Master"-"Slave",整个数据库的架构就变成了下面的”双树“结构,

点看全图

“双树”架构并没有支撑太久的时间,大概6个月后,随着用户的激增,系统再一次达到了极限,不仅”写”操作成为了瓶颈,而且“异步复制"也由于 ”Slave“服务器过于繁忙而出现了严重的滞后而造成读数据的不一致。那么,能不能在现有架构加以解决,比如说增加新的”Master“服务器和考虑采 用”同步复制“呢?答案是否定的,在Master超过两台的设置中,只能采用”闭环链“的方式进行复制,在大数据量的生产环境中,很容易造成在任意时刻没 有一个Master或Slave节点是具有全部最新数据的(有点类似于”人一次也不能踏进同一条河“?),这样很难保障数据的一致性,而且一旦其中一个Master出现故障,将中断整个复制链;而对于”同步复制“,当然这是消除”复制滞后“的最好办法,不过在当时MySQL的同步复制还远没有成熟到可以运用在投产环境中。

Flickr网站的架构,需要一次大的变化来解决长期持续扩展的问题。

Shard - 大型网站数据库扩展的终极武器?

2005年7月,另一位大牛(MySQL 2005、2006年度 "Application of the Year Award"获得者)Dathan Pattishall加入了Flickr团队。一个星期之内,Dathan解决了Flickr数据库40%的问题,更重要的是,他为Flickr引进了 Shard架构,从而使Flickr网站具备了真正“线性”Scale-Out的增长能力,并一直沿用至今,取得了巨大的成功。
Shard主要是为了解决传统数据库Master/Slave模式下单一Master数据库的“写”瓶颈而出现的,简单的说Shard就是将一个大表分 割成多个小表,每个小表存储在不同机器的数据库上,从而将负载分散到多个机器并行处理而极大的提高整个系统的“写”扩展能力。相比传统方式,由于每个数据 库都相对较小,不仅读写操作更快,甚至可以将整个小数据库缓存到内存中,而且每个小数据库的备份,恢复也变得相对容易,同时由于分散了风险,单个小数据库 的故障不会影响其他的数据库,使整个系统的可靠性也得到了显著的提高。
对于大多数网站来说,以用户为单位进行Shard分割是最合适不过的,常见的分割方法有按地域(比如邮编),按Key值(比如Hash用户ID),这些 方法可以简单的通过应用配置文件或算法来实现,一般不需要另外的数据库,缺点是一旦业务增加,需要再次分割Shard时要修改现有的应用算法和重新计算所 有的Shard KEY值;而最为灵活的做法是以“目录”服务为基础的分割,即在Shard之前加一个中央数据库(Global Lookup Cluster),应用要先根据用户主键值查询中央数据库,获得用户数据所在的Shard,随后的操作再转向Shard所在数据库,例如下图:

点看全图

而应用的主要修改在于要添加一个Lookup访问层,例如将以下的代码:

1
2
3
string connectionString = @"Driver={MySQL};SERVER=dbserver;DATABASE=CustomerDB;";
OdbcConnection conn = new OdbcConnection(connectionString);
conn.Open();
变为:

1
2
3
string connectionString = GetDatabaseFor(customerId);
OdbcConnection conn = new OdbcConnection(connectionString);
conn.Open();
GetDatabaseFor()函数完成根据用户ID获取Shard connectionString的作用。
对应我们前面所提到过的Flickr架构

点看全图

Dual Tree Central Database就是中央数据库,存放用户表,记录的信息是用户主键以及此用户对以的数据库Shard区;而Master-Master Shards就是一个个的Shard用户数据库,存储实际的用户数据和照片的元数据(Meta Data)。

Flickr Shard的设计我们在Flickr 网站架构研究(1)中已经总结过了,在此不再赘述。我们在此谈一下Shard架构的主要问题和Flickr的解决办法:1)Shard只适用于不需要 join操作的表,因为跨Shard join操作的开销太大,解决的办法是将一个用户的所有数据全部存放在同一个Shard里,对于一些传统方式下需要 跨Shard查询的数据,只能采取冗余的方法,比如Shard1的用户A对Shard2的用户B的照片进行了评论,那么这条评论将同时存放在Shard1 和Shard2中。这样就存在一个数据一致性的问题,常规的做法是用数据库事务(Transaction)、”两阶段提交“(2 phase commit)来解决,但做过两阶段提交(2PC)应用的都知道,2PC的效率相对较差,而且实际上也不能100%保证数据的完整性和一致性;另外,一旦 由于其中一个Shard故障而提交失败回滚,用户只能放弃或再试一遍,用户体验较差。Flickr对于数据一致性的解决方案是Queue(Flickr用 PHP开发了一个强大的Queue系统,将所有可以异步的任务都用Queue来实现,每天处理高达1千万以上的任务。),事实上当用户A对用户B的照片进 行评论时,他并不关心这条评论什么时候出现在用户B的界面上,即将这条评论添加到用户B的交易是可以异步的,允许一定的迟延,通过Queue处理,既保证 了数据的一致性,又缩短了用户端的相应时间,提高了系统性能。2)Shard的另一个主要问题Rebalancing,既当现有Shard的负载达到一定 的阀值,如何将现有数据再次分割,Flickr目前的方式依然是手工的,既人工来确定哪些用户需要迁移,然后运行一个后台程序进行数据迁移,迁移的过程用 户账户将被锁住。(据说Google做到了完全自动的Rebalancing,本着”萨大“坑里不再挖坑的原则,如果有机会的话,留到下一个系列再研究 吧)

Memcached的应用和争论

大家应该已经注意到,Flickr为中央数据库配置了Memcached作为数据库缓存,接下来的问题是,为什么用Memcached?为什么 Shard不需要Memcached?Memcached和Master,Slave的关系怎样?笔者将试图回答这些问题供大家参考,网上的相关争论很 多,有些问题尚未有定论。
Memecached是一个高性能的,分布式的,开源的内存对象缓存系统,顾名思义,它的主要目的是将经常读取的对象放入内存以提高整个系统,尤其是数 据库的扩展能力。Memcached的主要结构是两个Hash Table,Server端的HashTable以key-value pair的方式存放对象值,而Client端的HashTable的则决定某一对象存放在哪一个Memcached Server.举个例子说,后台有3个Memecached Server,A、B、C,Client1需要将一个对象名为”userid123456“,值为“鲁丁"的存入,经过Client1的Hash计 算,"userid123456"的值应该放入Memcached ServerB, 而这之后,Client2需要读取"userid123456"的值,经过同样的Hash计算,得出"userid123456"的值如果存在的话应该在 Memcached ServerB,并从中取出。最妙的是Server之间彼此是完全独立的,完全不知道对方的存在,没有一个类似与Master或Admin Server的存在,增加和减少Server只需在Client端"注册"并重新Hash就可以了。
Memcached作为数据库缓存的作用主要在于减轻甚至消除高负载数据库情况下频繁读取所带来的Disk I/O瓶颈,相对于数据库自身的缓存来说,具有以下优点:1)Memecached的缓存是分布式的,而数据库的缓存只限于本机;2)Memcached 缓存的是对象,可以是经过复杂运算和查询的最终结果,并且不限于数据,可以是任何小于1MB的对象,比如html文件等;而数据库缓存是以"row"为单 位的,一旦"row"中的任何数据更新,整个“row"将进行可能是对应用来说不必要的更新;3)Memcached的存取是轻量的,而数据库的则相对较 重,在低负载的情况下,一对一的比较,Memcached的性能未必能超过数据库,而在高负载的情况下则优势明显。
Memcached并不适用于更新频繁的数据,因为频繁更新的数据导致大量的Memcached更新和较低的缓冲命中率,这可能也是为什么Shard没 有集成它的原因;Memcached更多的是扩展了数据库的”读“操作,这一点上它和Slave的作用有重叠,以至于有人争论说应该 让"Relication"回到它最初的目的”Online Backup"数据库上,而通过Memcached来提供数据库的“读”扩展。(当然也有人说,考虑到Memcached的对应用带来的复杂性,还是慎 用。)
然而,在体系架构中增加Memecached并不是没有代价的,现有的应用要做适当的修改来同步Memcached和数据库中的数据,同时Memcached不提供任何冗余和“failover”功能,这些复杂的控制都需要应用来实现。基本的应用逻辑如下:

对于读操作:

1
2
3
4
5
$data = memcached_fetch( $id );
return $data if $data
$data = db_fetch( $id );
memcached_store( $id, $data );
return $data;
对于写操作:
1
2
db_store( $id, $data );
memcached_store( $id, $data );
我们看到在每一次数据更新都需要更新Memcached,而且数据库或Memcached任何一点写错误应用就可能取得“过期”的数据而得到错误的结果,如何保证数据库和Memcached的同步呢?

复制滞后和同步问题的解决

我们知道复制滞后的主要原因是数据库负载过大而造成异步复制的延迟,Shard架构有效的分散了系统负载,从而大大减轻了这一现象,但是并不能从根本上消除,解决这一问题还是要靠良好的应用设计。
当用户访问并更新Shard数据时,Flickr采用了将用户“粘”到某一机器的做法,
$id = intval(substr($user_id, -10));
$id % $count_of_hosts_in_shard
即同一用户每次登录的所有操作其实都是在Shard中的一个Master上运行的,这样即使复制到Slave,也就是另一台Master的时候有延时,也不会对用户有影响,除非是用户刚刚更新,尚未复制而这台Master就出现故障了,不过这种几率应该很小吧。
对于Central Database的复制滞后和同步问题,Flickr采用了一种复杂的“Write Through Cache"的机制来处理:

点看全图

"Write Through Cache"就是将所有的数据库”写“操作都先写入”Cache",然后由Cache统一去更新数据库的各个Node,“Write Through Cache"维护每一个Node的更新状态,当有读请求时,即将请求转向状态为”已同步“的Node,这样即避免了复制滞后和Memcached的同步问 题,但缺点是其实现极为复杂,“Write Throug Cache"层的代码需要考虑和实现所有”journal","Transaction“,“failover”,和“recovery"这些数据库已经 实现的功能,另外还要考虑自身的"failover"问题。我没有找到有关具体实现的说明,只能猜测这一部分的处理可能也是直接利用或是实现了类似于 Flickr的Queue系统吧。

3、YouTube架构总结

这个貌似在国内是被和谐的,要fan qiang才能访问(不知到底何故)。看看他的架构:
--NetScaler用于负载均衡和静态内容缓存

--使用lighttpd作为Web服务器来提供视频服务

--CDN在多个地方备份内容,这样内容离用户更近的机会就会更高

--使用Google的BigTable,一个分布式数据存储、数据库分成shards,不同的用户指定到不同的shards、使用BigTable将图片备份到不同的数据中心,代码查看谁是最近的

这里是YouTube详细架构描述

YouTube架构学习

原文: YouTube Architecture

YouTube发展迅速,每天超过1亿的视频点击量,但只有很少人在维护站点和确保伸缩性。

平台
Apache
Python
Linux(SuSe)
MySQL
psyco,一个动态的Python到C的编译器
lighttpd代替Apache做视频查看

状态
支持每天超过1亿的视频点击量
成立于2005年2月
于2006年3月达到每天3千万的视频点击量
于2006年7月达到每天1亿的视频点击量
2个系统管理员,2个伸缩性软件架构师
2个软件开发工程师,2个网络工程师,1个DBA

处理飞速增长的流量
Java代码 复制代码 收藏代码
  1. while (true)
  2. {
  3. identify_and_fix_bottlenecks();
  4. drink();
  5. sleep();
  6. notice_new_bottleneck();
  7. }

每天运行该循环多次

Web服务器
1,NetScaler用于负载均衡和静态内容缓存
2,使用mod_fast_cgi运行Apache
3,使用一个Python应用服务器来处理请求的路由
4,应用服务器与多个数据库和其他信息源交互来获取数据和格式化html页面
5,一般可以通过添加更多的机器来在Web层提高伸缩性
6,Python的Web层代码通常不是性能瓶颈,大部分时间阻塞在RPC
7,Python允许快速而灵活的开发和部署
8,通常每个页面服务少于100毫秒的时间
9,使用psyco(一个类似于JIT编译器的动态的Python到C的编译器)来优化内部循环
10,对于像加密等密集型CPU活动,使用C扩展
11,对于一些开销昂贵的块使用预先生成并缓存的html
12,数据库里使用行级缓存
13,缓存完整的Python对象
14,有些数据被计算出来并发送给各个程序,所以这些值缓存在本地内存中。这是个使用不当的策略。应用服务器里最快的缓存将预先计算的值发送给所有服务器也花不了多少时间。只需弄一个代理来监听更改,预计算,然后发送。

视频服务
1,花费包括带宽,硬件和能源消耗
2,每个视频由一个迷你集群来host,每个视频被超过一台机器持有
3,使用一个集群意味着:
-更多的硬盘来持有内容意味着更快的速度
-failover。如果一台机器出故障了,另外的机器可以继续服务
-在线备份
4,使用lighttpd作为Web服务器来提供视频服务:
-Apache开销太大
-使用epoll来等待多个fds
-从单进程配置转变为多进程配置来处理更多的连接
5,大部分流行的内容移到CDN:
-CDN在多个地方备份内容,这样内容离用户更近的机会就会更高
-CDN机器经常内存不足,因为内容太流行以致很少有内容进出内存的颠簸
6,不太流行的内容(每天1-20浏览次数)在许多colo站点使用YouTube服务器
-长尾效应。一个视频可以有多个播放,但是许多视频正在播放。随机硬盘块被访问
-在这种情况下缓存不会很好,所以花钱在更多的缓存上可能没太大意义。
-调节RAID控制并注意其他低级问题
-调节每台机器上的内存,不要太多也不要太少

视频服务关键点
1,保持简单和廉价
2,保持简单网络路径,在内容和用户间不要有太多设备
3,使用常用硬件,昂贵的硬件很难找到帮助文档
4,使用简单而常见的工具,使用构建在Linux里或之上的大部分工具
5,很好的处理随机查找(SATA,tweaks)

缩略图服务
1,做到高效令人惊奇的难
2,每个视频大概4张缩略图,所以缩略图比视频多很多
3,缩略图仅仅host在几个机器上
4,持有一些小东西所遇到的问题:
-OS级别的大量的硬盘查找和inode和页面缓存问题
-单目录文件限制,特别是Ext3,后来移到多分层的结构。内核2.6的最近改进可能让Ext3允许大目录,但在一个文件系统里存储大量文件不是个好主意
-每秒大量的请求,因为Web页面可能在页面上显示60个缩略图
-在这种高负载下Apache表现的非常糟糕
-在Apache前端使用squid,这种方式工作了一段时间,但是由于负载继续增加而以失败告终。它让每秒300个请求变为20个
-尝试使用lighttpd但是由于使用单线程它陷于困境。遇到多进程的问题,因为它们各自保持自己单独的缓存
-如此多的图片以致一台新机器只能接管24小时
-重启机器需要6-10小时来缓存
5,为了解决所有这些问题YouTube开始使用Google的BigTable,一个分布式数据存储:
-避免小文件问题,因为它将文件收集到一起
-快,错误容忍
-更低的延迟,因为它使用分布式多级缓存,该缓存与多个不同collocation站点工作
-更多信息参考Google ArchitectureGoogleTalk ArchitectureBigTable

数据库
1,早期
-使用MySQL来存储元数据,如用户,tags和描述
-使用一整个10硬盘的RAID 10来存储数据
-依赖于信用卡所以YouTube租用硬件
-YouTube经过一个常见的革命:单服务器,然后单master和多read slaves,然后数据库分区,然后sharding方式
-痛苦与备份延迟。master数据库是多线程的并且运行在一个大机器上所以它可以处理许多工作,slaves是单线程的并且通常运行在小一些的服务器上并且备份是异步的,所以slaves会远远落后于master
-更新引起缓存失效,硬盘的慢I/O导致慢备份
-使用备份架构需要花费大量的money来获得增加的写性能
-YouTube的一个解决方案是通过把数据分成两个集群来将传输分出优先次序:一个视频查看池和一个一般的集群
2,后期
-数据库分区
-分成shards,不同的用户指定到不同的shards
-扩散读写
-更好的缓存位置意味着更少的IO
-导致硬件减少30%
-备份延迟降低到0
-现在可以任意提升数据库的伸缩性

数据中心策略
1,依赖于信用卡,所以最初只能使用受管主机提供商
2,受管主机提供商不能提供伸缩性,不能控制硬件或使用良好的网络协议
3,YouTube改为使用colocation arrangement。现在YouTube可以自定义所有东西并且协定自己的契约
4,使用5到6个数据中心加CDN
5,视频来自任意的数据中心,不是最近的匹配或其他什么。如果一个视频足够流行则移到CDN
6,依赖于视频带宽而不是真正的延迟。可以来自任何colo
7,图片延迟很严重,特别是当一个页面有60张图片时
8,使用BigTable将图片备份到不同的数据中心,代码查看谁是最近的

学到的东西
1,Stall for time。创造性和风险性的技巧让你在短期内解决问题而同时你会发现长期的解决方案
2,Proioritize。找出你的服务中核心的东西并对你的资源分出优先级别
3,Pick your battles。别怕将你的核心服务分出去。YouTube使用CDN来分布它们最流行的内容。创建自己的网络将花费太多时间和太多money
4,Keep it simple!简单允许你更快的重新架构来回应问题
5,Shard。Sharding帮助隔离存储,CPU,内存和IO,不仅仅是获得更多的写性能
6,Constant iteration on bottlenecks:
-软件:DB,缓存
-OS:硬盘I/O
-硬件:内存,RAID
7,You succeed as a team。拥有一个跨越条律的了解整个系统并知道系统内部是什么样的团队,如安装打印机,安装机器,安装网络等等的人。With a good team all things are possible。

4、PlentyOfFish架构总结

这个我觉的最神奇了,一个人每天花2个小时,可以维护一个每天3000W PV的,而且是基于.NET的(呵呵,终于给我们.net程序员一个好榜样了)。简述他的架构:
--用Microsoft Windows操作系统作为服务器

--使用ASP.NET技术

--使用IIS作为Web容器

--用Akamai CDN来缓存网页

--用Foundry ServerIron 来做负载均衡

--sqlserver采用master-slave架构,两台负责read操作,master那台负责写操作

--所有的request数据都使用了gzip压缩

PlentyOfFish详细架构描述

PlentyOfFish.com .NET网站的又一传奇

PlentyOfFish(以下简称POF)是一家在美国广受欢迎的婚介交友网站,平均每月有4千5百万的访问者,每天有3千万的访问量(这是前一段时间的数据了),但你万万想不到的是,这个被估值$1000000000的网站却只有一个人每天只干两小时活。

POF对网友是100%免费的,所有的收入来自于Google广告点击,不像中国有的婚介交友网站广告纷乱,POF只有一个广告通栏,此外没有任何弹出广告,感觉非常的简洁。 它的成功的关键因素可能就是在基本功能方面能很符合用户的需要,在UE方面做的也比较贴心,同时也让用户能够坦然接受这个免费网站的UI的丑陋和服务的不稳定性,而更为愿意通过这个平台来发布一些内容,share一些个人图片,通过这个网站来找靓妞或者帅哥dating了。

我们先暂且不谈他在用户体验上是如何胜出的,光是系统架构上就值得我们好好体味一下了,毕竟一个人花那么少点时间就可以维护如此庞大的系统,由此可见其架构是如此的简单、灵活、高效。那我们就简单来分析他的架构吧。

  1. 用Microsoft Windows操作系统作为服务器
    该网站采用的是Windows x64 Server 2003。采用Windows的原因是并不是站长认为Windows适合POF,而是因为站长本人建站时候的技术很差,完全不会使用Linux和 Unix。他办这个网站的初衷其实是学习ASP。也因为如此,整个网站的标准就是简单、简单、再简单。对于大流量负载均衡的处理,站长目前没有使用 Windows 的负载均衡Network Load Balancing (NLB),他认为NLB不能保持sessions状态。对于不能保持sessions状态,倒也可以存储session状态到数据库,或者共享文件系统。8-12个NLB服务器可以共同放入一个farm,而且farm的数量也是没有限制的。然后将一个 DNS轮转调度策略(round-robin scheme)用在farm之间。其实这样的架构,也曾经一度被用在POF——总计70个前端Web服务器(front end web servers),可以支持30万人的并发访问。NLB也是一个不错的选择。但是这样的软件解决方案显得有点贵,而且很麻烦,最终站长选择了硬件来完成负载均衡任务。
  2. 使用ASP.NET技术
    ASP.NET中的缓存功能完全没有启用。因为该网站的动态特性,往往还没等缓存储存,数据就已经改变或消失了。另外,该站点也没有用ASP.NET开发什么组件,所有的组件都是现成的,一切都以简单出发。
  3. 使用IIS作为Web容器
    由于IIS限制了最大64000的连接数,所以POF不得不添加负载均衡器来处理为数众多的并发连接。站长曾经考虑过添加第二IP,并采用轮转调度(Round-Robin)来解决访问量过大的问题,但是这样太过复杂,有悖于一个人的简单管理,最后被放弃了。其实用多个Web服务器就可以简单解决。
  4. 用Akamai CDN来缓存网页
    该站点部署了Akamai CDN(网页缓存加速),每天处理大约1亿幅图片的缓存加速。CDN的原理是将你站点部分的内容,分发到CDN服务商的服务器上,因为CDN服务商广泛分布的服务器可以更加接近最终用户的地域,这样速度就会更快。假如你当前的POF页面有8幅图片,每幅图片的下载需要100毫秒,那么光下载这些图片就需要花上一秒钟。所以分配这些图片到离用户更近的区域是非常必要的,而且CDN也一定程度缓解了不同网络服务商之间的线路差异。当然,也不是所有的图片都采用了CDN,一些小于2KB的图片还是缓存于本地内存。可能因为部署了CDN,POF虽然是国外网站,但速度却非常快,与国内网站无二。有关CDN技术,可以参考http://baike.baidu.com/view/21895.htm
  5. 用Foundry ServerIron 来做负载均衡
    POF采用了网捷网络公司的Web交换器ServerIron,ServerIron 能够有效地处理超过 16,000,000个并发连接,而且能够改善服务器负载均衡和缓冲转换。正如上文所述,最终站长放弃了NLB而采用了ServerIron 负载均衡,经过详细计算之后,他发现部署ServerIron要比NLB便宜。其实也不只是POF,很多大网站都采用ServerIron来处理TCP 连接pooling和bot自动监察。ServerIron除了负载均衡还能做很多事情,因此还是值得的考虑的。
  6. 数据库优化
    3台SQL Server,采用master-slave架构,两台负责read操作,master那台负责写操作。这个和myspace早期的后台数据库架构是一样,看来这种架构很流行嘛。POF 有一个主要的数据库,两个搜索数据库。监测使用任务管理器来完成。过去,有些问题会将数据库堵塞,其实这都是数据库自己的问题,好在POF没用.net的 library,找出问题相对容易一些。不过假如你使用了framework的很多层级,找出问题就可能很困难了。对于POF而言,数据库不仅仅是不出问题,还需要稳定和快速。由于POF网站的动态特性,基本用不到缓存,所以站长几年来花了很大功夫,在很多细节上优化了数据库,让数据库的相应更加迅速。
  7. Memory和CPU
    把最近常使用的图片直接放在内存中,所以内存会那么大;CPU配置也挺好,gzip是相当耗费CPU计算的。

    Markus说他碰到问题基本上是IO操作方面的瓶颈,很少是被.Net block住。Markus在Session,Farm,以及数据库反范式等很多方面都有很不错的经验,很值得我们学习和借鉴,更多的细节大家可以参考后面的链接的几篇文章。

  8. 压缩
    所有的request数据都使用了gzip压缩,大概耗费了30%的CPU,但是降低了带宽成本。欧美的带宽不便宜。

中国的经济环境持续向好,所以很多公司的IT部门都得到了更多的预算,但这些预算被合理的使用了吗?这些预算往往被用来采购更好的服务器硬件,更新的操作系统和数据库软件,还有各式各样的行业应用。倒不是说这些部署不好,只是说我们的IT部署一定要以实用为出发点,少做一些可有可无的投资。我们要多向 POF学习,其实稳定、快速、便捷才是制胜的关键。正如POF站长一再强调的简单哲学,所有复杂的东西都尽量不去使用。


虽然站长一开始的技术一般,但是随着建站时间的加长,他现在也已经是一个网站架构专家,他花了很多力气来优化数据库和维护系统,而且他也采用了CDN来加快不同地域的用户访问网站空间。不过其实该网站的搭建可以更加合理,比如可以采用S3来外包其图片存储,采用更轻质化的操作系统或者Web服务器等等。这些年来,类似于这样的建议非常多,但是站长还是坚持了他的简单策略,而且也拒绝对主页面进行美工优化,因为他认为多余的工作只会引来他人反感,实用才是关键。可见,保持简单性,和持续勤劳的维护是服务器运行良好的法宝。

相信我们可以从POF上学习的东西还有很多,毕竟该网站以一己之力,达到了史无前例的高度,净利润竟然逼近了500多人的大型IT网站。POF的成功必然有它的深刻理由,不仅是网站的整体的服务器和软件架构、很多细节的处理也同样值得我们借鉴。

5、WikiPedia架构总结

维基百科(Wikipedia)是一个基于Wiki技术的全球性多语言百科全书协作计划,同时也是一部在网际网路上呈现的网路百科全书,其目标及宗旨是为全人类提供自由的百科全书──用他们所选择的语言来书写而成的,是一个动态的、可自由和的全球知识体。

使用MediaWiki软件

--