a说了开发模式,再说说用到的Java MVC框架,当时的struts1.x
是用得比较多的框架,但是用过webwork和struts2的人可能知道,
struts1.x在多人协作方面有很多致命的弱点,由于没有一个轻量框
架作为基础,因此,很难扩展,这样架构师对于基础功能和全局
功能的控制就很难做到。而阿里巴巴的18个创始人之中,有个架
构师周悦虹,他在Jakarta Turbine的基础上做了很多扩展,打造了
一个阿里巴巴自己用的MVC框架WebX ( http://www.openwebx.
org/docs/Webx3_Guide_Book.html ),这个框架易于扩展,方
便组件化开发,它的页面模板支持JSP和Velocity等,持久层支持
ibatis和hibernate等,控制层可以用EJB和Spring(Spring是后来才有
的)。项目组选择了这个强大的框架。另外,当时Sun在全世界大
力推广他们的EJB,虽然淘宝的架构师认为这个东西用不到,但他
们还是极力坚持。在经历了很多次的技术讨论、争论甚至争吵之
后,这个系统的架构就变成了下图的形式。
34
34
淘宝技术这十年
MVC框架是阿里的WebX,控制层用了EJB,持久层是ibatis。
另外,为了缓解数据库的压力,商品查询和店铺查询放在搜索引
擎中。这个架构图是不是好看了一点了?
Sun的这帮工程师开发完淘宝的网站之后,用同样的架构又做
了一个很牛的网站,叫“支付宝”。(上一篇说过支付宝最初是
淘宝上的“安全交易”功能,这个功能后来独立出来,成立了一
个网站,也成立了一个公司,就是现在的支付宝。把支付宝从淘
宝分出去的人,就是Sun公司的这几个人。)下图是支付宝的第一
次员工大会。
35
35
第二部分:淘宝技术发展2
上面的架构中,引入了搜索引擎iSearch(前文说过,iSearch
其实是在LAMP系统运行一段时间之后被多隆引进的,换为Oracle
之后只是替换一下数据源)。其实这个搜索引擎的原理很简单,
就是把数据库里的数据dump(倾倒)成结构化的文本文件后,放
在硬盘上,提供Web应用以约定的参数和语法来查询这些数据。
这看起来不难,难的是数以亿计的信息,怎么做到快速更新呢?
这好比你做了一个网站,在百度上很快就能搜到,你一定很满意
了。但如果你发布一件商品,在淘宝上过1个小时还搜不到,你肯
定要郁闷了。另一个难点是如何保证非常高的容量和并发量?再
往后面就要考虑断句和语义分析的问题,以及推荐算法等更加智
能的问题。这些内容先不详细介绍,因为搜索引擎的技术已经足
以写好几本书了。
36
36
淘宝技术这十年
其实在任何时候,开发语言本身都不是系统的瓶颈,业务带
来的压力更多的存在于数据和存储方面。前面也说到,MySQL撑
不住之后换为Oracle,Oracle的存储一开始在本机上,后来在NAS
上,NAS撑不住了用EMC的SAN存储,再后来,Oracle的RAC撑
不住了,数据的存储方面就不得不考虑使用小型机。在2004年夏
天,DBA七公、测试工程师郭芙和架构师行癫,踏上了去北京测
试小型机的道路。他们带着小型机回来的时候,我们像欢迎领袖
一样欢迎他们,因为那是我们最值钱的设备,价格表上的数字吓
死人。小型机买回来之后,我们争相合影,然后Oracle就运行在
了小型机上,存储方面,从EMC低端CX存储到Sun oem hds高端
存储,再到EMC dmx高端存储,一级一级地往上跳。
到2004年底,淘宝网已经有4百多万种商品了,日均4千多万
个PV,注册会员达400万个,全网成交额达10亿元。
到现在为止,我们已经用上了IBM的小型机、Oracle的数据
库、EMC的存储,这些东西都是很贵的,那些年可以说是花钱
如流水。有人说过“钱能解决的问题,就不是问题”,但随着淘
宝网的发展,在不久以后,钱已经解决不了我们的问题了。花钱
买豪华的配置,也许能支持1亿个PV的网站,但淘宝网的发展实
在是太快了,到了10亿个PV怎么办?到了百亿怎么办?在几年以
后,我们不得不创造技术,解决这些只有世界顶尖的网站才会遇
到的问题。后来我们在开源软件的基础上进行自主研发,一步一
37
37
第二部分:淘宝技术发展2
步地把IOE(IBM小型机、Oracle、EMC存储)这几个“神器”
都去掉了。这些神器就如同《西游记》中那些神仙的兵器,他们
身边的妖怪们拿到这些兵器能把猴子打得落荒而逃。但最牛的神
仙是不依赖这些神器的,他们挥一挥衣袖、翻一下手掌就威力无
比了。
坚若磐石
已经有读者在迫不及待地问怎么去掉了IOE?别急,在去掉
IOE之前还有很长的路要走(在后面讲到TDDL的时候,会提到
去IOE的一些事情)。行癫等人买回小型机之后,我们用上了
38
38
淘宝技术这十年
Oracle。然后七公带着一帮DBA做优化SQL和存储方面的工作,行
癫带着几个架构师研究数据库的扩展性。Oracle本身是一个封闭
的系统,用Oracle怎么做扩展呢?用现在一个时髦的说法就是做
“分库分表”。
我们知道,一台Oracle的处理能力是有上限的,它的连接池有
数量限制,查询速度与容量成反比。简单地说,在数据量上亿、
查询量上亿的时候,就到它的极限了。要突破这种极限,最简单
的方式就是多用几个Oracle数据库。但一个封闭的系统做扩展,
不像分布式系统那样直接加机器就可以了。我们的做法是把用户
的信息按照ID来存放到两个数据库中(DB1和DB2),把商品的
信息和卖家信息放在两个对应的数据库中,把商品类目等通用信
息放在第三个库中(DBcommon)。这么做的目的是除了增加了
数据库的容量之外,还有一个就是做容灾,即万一一个数据库挂
了,整个网站上还有一半的商品可以买。
数据库这么分后,应用程序就会出现麻烦,如果我是卖家,
查看我的商品没有问题,我们都在一个库里。但如果我是一个
买家,买的商品有DB1的,也有DB2的,要查看“我已买到的宝
贝”的时候,应用程序怎么办?必须到两个数据库中分别查询对
应的商品。要按时间排序怎么办?两个库中“我已买到的宝贝”
全部查出来在应用程序中做合并。另外,分页怎么处理?关键字
查询怎么处理?专业点的说法就是数据的Join没法做了。这些工
作交给程序员来做也许会更麻烦,于是行癫出手了,他写了一个
39
39
第二部分:淘宝技术发展2
数据库路由的框架DBRoute,统一处理了数据的合并、排序、分
页等操作,让程序员像使用一个数据库一样操作多个数据库里的
数据,这个框架在淘宝的Oracle时代一直在使用。但是后来随着
业务的发展,这种分库的第二个目的——“容灾”的效果没有达
到。像评价、投诉、举报、收藏、我的淘宝等很多地方,都必须
同时连接DB1和DB2,哪个库挂了都会导致整个网站挂掉。
上一篇说过,采用EJB其实是和Sun的工程师妥协的结果,在
他们离开之后,EJB也逐渐被冷落了下来。在2005年和2006年的时
候,Spring大放异彩,于是在控制层用Spring替换掉了EJB,给整
个系统精简了很多代码。
40
40
淘宝技术这十年
上一篇还说过,为了减少数据库的压力,提高搜索的效率,
我们引入了搜索引擎。随着数据量的继续增长,到了2005年,商
品数有1663万个,PV有8931万个,注册会员有1390万个,这给数
据存储带来的压力依然很大,数据量大,速度就慢。亲,除了搜
索引擎、分库分表,还有什么办法能提升系统的性能?一定还有
招数可以用,这就是缓存和CDN(内容分发网络)。
你可以想象,9000万次的访问量,有多少是在商品详情页
面?访问这个页面的时候,数据全都是只读的(全部从数据库中
读出来,不写入数据库),在那个时候,我们的架构师多隆大神
做了一个基于 Berkeley DB 的缓存系统,把很多不太变动的只读
信息放了进去。其实最初这个缓存系统还比较弱,我们并不敢把
所有能缓存的信息都往里面放,一开始先把卖家的信息放里面,
然后把商品属性放里面,再把店铺信息放里面,但是像商品详情
这类字段太大的放进去受不了。说到商品详情,这个字段比较恐
怖,有人统计过,淘宝商品详情打印出来平均有5米长,在系统里
其实放在哪里都不招人待见。笔者清楚地记得,我来淘宝之后担
任项目经理做的第一个项目就是把商品详情从商品表中移出来。
它最早与商品的价格、运费等信息放在一个表中,拖慢了整张表
的查询速度,而很多时候查询商品信息是不需要查看详情的。于
是在2005年的时候,我把商品详情放在数据库的另外一张表中,
再往后,这个大字段被从数据库中请了出来,先是放入了缓存系
统,到现在是放进了文件系统TFS中。
41
41
第二部分:淘宝技术发展2
到现在为止,整个商品详情的页面都在缓存里面了,眼尖的
读者可能会发现现在的商品详情不全是“只读”的信息了,这个
页面上有个信息叫“浏览量”(这个信息是2006年加上去的),
这个数字每刷新一次,页面就要“写入”存储一次,这种高频度
实时更新的数据能用缓存吗?通常来说,这种是必须放进数据库
的,但是悲剧的是,我们在2006年开发这个功能的时候,把浏览
量写入数据库,发布上线1个小时后,数据库就挂掉了,每天几亿
次的写入,数据库承受不了。那怎么办?亲,……先不回答你,
后面讲到缓存Tair的时候再说。(下图不是广告,请把注意力从
左边移到右边中间,看看浏览量这个数据在哪里。)
CDN这个工作相对比较独立,跟别的系统一样,一开始我们
采用的也是商用系统。后来随着流量的增加,商用的系统已经撑
不住了,LVS的创始人章文嵩博士带人搭建了淘宝自己的CDN网
络。在本文的引言中,我说过淘宝的CDN系统支撑了800Gbps以
上的流量,作为对比,我们可以看一下国内专业做CDN的上市公
司ChinaCache的介绍——“ChinaCache是中国第一的专业CDN服
务提供商,向客户提供全方位网络内容快速分布解决方案。作为
首家获信产部许可的CDN服务提供商,目前ChinaCache在全国50
多个大中城市拥有近300个节点,全网处理能力超过500Gbps,其
CDN网络覆盖中国电信、中国网通、中国移动、中国联通、中国
铁通和中国教育科研网等各大运营商。”——淘宝一家的流量比
他们的加起来还要多,这样你可以看出淘宝在CDN上的实力,这
在全世界都是数一数二的(其实我们一开始用的商用CDN就是
ChinaCache,它们支撑了很长时间)。另外,因为CDN需要大量
的服务器,要消耗很多能源(消耗多少?在前两年我们算过一笔
账,淘宝上产生一个交易,消耗的电量足以煮熟4个鸡蛋)。这两
年,章文嵩的团队又在研究低功耗的服务器,在绿色计算领域也
做了很多开创性的工作,我们定制的基于英特尔凌动处理器的低
功耗服务器已经部署到了CDN机房,降低了很大的能耗。这方面
的内容可以看后面笔者对章文嵩博士的专访。
回想起刚用缓存那段时间,笔者还是个菜鸟,有一个经典的
错误常常犯,就是更新数据库的内容时,忘记通知缓存系统,结
果在测试的时候就发现我改过的数据在页面上没有变化。后来做
了一些页面上的代码,修改CSS和JS的时候,用户本地缓存的信息
没有更新,页面上也会乱掉,在论坛上被人说的时候,我告诉他
用Ctrl+F5组合键(清除本地缓存刷新页面),然后赶紧修改脚本
43
43
第二部分:淘宝技术发展2
文件的名称,重新发布页面。
我们对数据分库、放弃EJB、引入Spring、加入缓存、加入
CDN等工作,看起来没有章法可循,其实都是围绕着提高容量、
提高性能、节约成本来做的,由于这些是不算大的版本变迁,我
们姑且叫它2.1版,这个版本从构图上看有三只脚,是不是稳定了
很多?
在这个稳定的版本下,淘宝网的业务有了突飞猛进的发展,
2005年5月,微软的MSN门户大张旗鼓地进入中国,淘宝网成为
它的购物频道。2005年中,盛大进军机顶盒业务,其电视购物的
功能也是淘宝网开发的。虽然因为水土不服或者政策的原因,这
44
44
淘宝技术这十年
两个业务现在都看不到了,但他们曾经是中国互联网行业的大事
件。那位和微软谈合作的人是@胖胡斐,他花起钱来也是大手笔
的,我们就管他叫“二少爷”,他现在是蘑菇街的COO。
另外,老马也从来都不缺少娱乐精神,他看到湖南卫视的超
女如此成功,也想借鉴一下这种模式。就在2005年底,淘宝网和
湖南卫视合作推出了一档节目,叫做“超级Buyer秀”。这是一
个定位于“超级会网购的人”的一个选秀节目,以百万年薪为诱
饵,让大家分享自己的网购经历,网友投票选出最终胜者。这个
从海选到表演,历时一年多,广告做得铺天盖地。虽然节目最终
没有超女那样火爆,这也让“淘宝网就是网购”的形象通过湖南
卫视更加深入人心。
45
45
第二部分:淘宝技术发展2
到2006年,淘宝网已经有了1.5亿个的日均PV,商品数达5千
多万个,注册用户3千多万个,全网成交额达169亿元。
创造技术
TFS
回顾一下上面几个版本,1.0版的PHP系统运行了将近一年
的时间(2003年5月—2004年1月),服务器由一台发展到多台;
46
46
淘宝技术这十年
后来数据库撑不住了,将MySQL换成了Oracle,引入了搜索引擎
(2004年1月—2004年5月,叫1.1版本);然后不到半年的时间又
把开发语言换成了Java(2004年2月—2005年3月,叫2.0版本),
数据服务逐步采用了IOE;随着数据量和访问量的增长,我们进
行数据分库、加入缓存、使用CDN(2004年10月—2007年1月,
叫2.1版本)。这几个版本中间有些时间上的重合,因为很多架构
的演化并没有明显的时间点,它是逐步进化而来的。
在描述2.1版本的时候,我写的标题是《坚若磐石》,这个
“坚若磐石”是因为这个版本终于稳定下来了,在这个版本的系
统上,淘宝网运行了两年多的时间。这期间有很多优秀的人才加
入,也开发了很多优秀的产品,例如,商品的类目属性、支付宝
认证系统、招财进宝项目、淘宝旅行、淘宝彩票、淘宝论坛等,
甚至在团购网站风起云涌之前,淘宝网在2006年就推出了“团
购”的功能。
在这些产品和功能的最底层,其实还是商品管理和交易管理
这两大功能。这两大功能在2.1版本中都有很大的变化。商品管理
起初是要求卖家选择7天到期还是14天到期,到期之后自动下架,
必须重新发布才能上架,上架之后就变成了新的商品信息(ID变
过了)。另外,如果商品在这个期间成交了,之后再有新货,必
须发布一个新的商品信息。这么做有几个原因,一是参照拍卖商
品的时间设置,要在某日期前结束挂牌;二是搜索引擎不知道同
47
47
第二部分:淘宝技术发展2
样的商品哪个排在前面,那就把挂牌时间长的排前面(这样就必
须在某个时间把老的商品下架,否则它会一直排在前面);第三
是成交信息和商品ID关联,这个商品如果多次编辑还是同一个ID
的话,成交记录中的商品信息会不断改变;还有一个不为人知的
原因是我们的存储有限,不能让所有的商品老存放在主库中。这
种处理方式简单粗暴,但还算是公平。不过这样会导致很多需求
都无法满足,例如,卖出一件商品之后就无法更改价格,否则前
面已经成交的那个价格都变了,而且同样的商品,上一次销售后
的很多好评都无法在下一个商品上体现出来;再如,我买过的商
品结束后只看到交易的信息,不知道卖家是否还会卖。基于这些
需求,我们在2006年下半年把商品和交易拆开,一个商家的一种
商品有一个唯一的ID,上下架都是同一个商品。那么如果卖家修
改价格和库存等信息,已成交的信息怎么处理?那就在买家每交
易一次的时候,都记录下商品的快照信息,有多少次交易就有多
少个快照。这样买卖双方比较爽了,但这给系统带来了什么?存
储的成本大幅度上升了!
存储的成本高到什么程度呢?数据库方面用了IOE,一套下
来就是千万级别的,那几套下来就是——好多钱啊。另外,淘宝
网还有很多文件需要存储,最主要的就是图片、商品描述、交易
快照,一个商品要包含几张图片和一长串的描述信息,而每一张
图片都要生成几张规格不同的缩略图。在2010年,淘宝网的后端
系统上保存着286亿个图片文件。图片在交易系统中非常重要,大
48
48
淘宝技术这十年
家常说“一张好图胜千言”、“无图无真相”,淘宝网的商品照
片,尤其是热门商品图片的访问流量是非常大的。在淘宝网整体
流量中,图片的访问流量要占到90%以上,而且这些图片平均大
小为17.45KB,小于8KB的图片占整体图片数量的61%,占整体系
统容量的11%。这么多的图片数据、这么大的访问流量,给淘宝
网的系统带来了巨大的挑战。对于大多数系统来说,最头疼的就
是大规模的小文件存储与读取,因为磁头需要频繁寻道和换道,
因此,在读取上容易带来较长的延时。在大量高并发访问量的情
况下,简直就是系统的噩梦。我们该怎么办?
同样的套路,在某个规模以下采用现有的商业解决方案,达
到某种规模之后,商业的解决方案无法满足,只有自己创造解
决方案了。对于淘宝的图片存储来说,转折点在2007年。这之
前,一直采用商用存储系统,应用NetApp公司的文件存储系统。
随着淘宝网的图片文件数量以每年3倍的速度增长,淘宝网后端
NetApp公司的存储系统也从低端到高端不断迁移,直至2006年,
即使是NetApp公司最高端的产品也不能满足淘宝网存储的要求。
从2006年开始,我们决定自己开发一套针对海量小文件存储的文
件系统,用于解决自身图片存储的难题。这标志着淘宝网从使用
技术到了创造技术的阶段。
2007年之前的图片存储架构如下图所示。
49
49
第二部分:淘宝技术发展2
在一次架构师大会上,章文嵩博士总结了几点商用存储系统
的局限和不足。
第一,商用存储系统没有对小文件存储和读取的环境进行有
针对性的优化;第二,文件数量大,网络存储设备无法支撑;第
三,整个系统所连接的服务器越来越多,网络连接数已经达到网
络存储设备的极限;第四,商用存储系统扩容成本高,10TB的存
储容量需要几百万元,而且存在单点故障,容灾和安全性无法得
到很好的保证。
谈到在商用系统和自主研发之间的经济效益方面的对比,章
文嵩博士列举了以下几点经验。
50
50
淘宝技术这十年
第一,商用软件很难满足大规模系统的应用需求,无论是存
储、CDN还是负载均衡,在厂商实验室端,都很难实现如此大的
数据规模测试。
第二,在研发过程中,将开源和自主开发相结合,会有更好
的可控性,若系统出了问题,完全可以从底层解决问题,系统扩
展性也更高。
第三, 在一定规模效应的基础上,研发的投入都是值得的。
下图演示的是一个自主研发和购买商用系统的投入产出比,实际
上,图中交叉点的左边,购买商用系统都是更加实际和经济性更
好的选择,只有在规模超过交叉点的情况下,自主研发才能收到
较好的经济效果。实际上,规模化达到如此程度的公司并不多,
不过淘宝网已经远远超过了交叉点。
投入
买/租
研发
规模
第四,自主研发的系统可在软件和硬件的多个层次之间不断
51
51
第二部分:淘宝技术发展2
优化。
历史总是惊人的巧合,在我们准备研发文件存储系统的时
候,Google走在了前面,2007年,他们公布了GFS(Google File
System)的设计论文,这给我们带来了很多借鉴的思路。随后我
们开发出了适合淘宝使用的图片存储系统(TaoBao File System,
TFS)。3年之后,我们发现历史的巧合比我们想象的还要神奇,
几乎跟我们同时,中国的另外一家互联网公司也开发了他们的文
件存储系统,甚至取的名字都一样——TFS,太神奇了!(猜猜
是哪家)
2007年6月,TFS正式上线运营。在生产环境中应用的集群规
模达到了200台PC Server(146GB×6 SAS 15KB Raid5),文件数
量达到上亿级别;系统部署存储容量为140TB;实际使用存储容
量为50TB;单台支持随机IOPS 200+,流量为3MB/s。
说到TFS的系统架构,首先要描述清楚业务需求,淘宝对图片
存储的需求大概可以描述如下:
文件比较小;并发量高;读操作远大于写操作;访问随机;
没有文件修改的操作;要求存储成本低;能容灾,能备份。显
然,应对这种需求时要用分布式存储系统;由于文件大小比较统
一,可以采用专有文件系统;由于并发量高,读写随机性强,需
要更少的I/O操作;考虑到成本和备份,需要用廉价的存储设备;
考虑到容灾,需要能平滑扩容。
52
52
淘宝技术这十年
参照GFS并做了大量的优化之后,TFS 1.0版的架构图如下。
从上面的架构图可看出:集群由一对Name Server和多台Data
Server构成,Name Server的两台服务器互为双机,这就是集群文
件系统中管理节点的概念。
在这个系统中:
yy每个Data Server运行在一台普通的Linux主机上;
yy以Block文件的形式存放数据文件(一个Block的大小一般为
64MB);
yyBlock存储多份是为了保证数据安全;
53
53
第二部分:淘宝技术发展2
yy利用ext3文件系统存放数据文件;
yy磁盘raid5做数据冗余;
yy文件名内置元数据信息,用户自己保存TFS文件名与实际文
件的对照关系,这使得元数据量特别小。
淘宝TFS文件系统在核心设计上最大的取巧在于,传统的集群
系统中元数据只有1份,通常由管理节点来管理,因而很容易成为
瓶颈。而对于淘宝网的用户来说,图片文件究竟用什么名字来保
存他们并不关心,因此,TFS在设计规划上考虑在图片的保存文
件名上暗藏了 一些元数据信息,例如,图片的大小、时间、访问
频次等信息,包括所在的逻辑块号。而在实际的元数据上,保存
的信息很少,因此,元数据结构非常简单。仅仅只需要一个FileID
就能够准确定位文件在什么地方。由于大量的文件信息都隐藏在
文件名中,整个系统完全抛弃了传统的目录树结构,因为目录树
开销最大。拿掉后,整个集群的高可扩展性可极大地提高。实际
上,这一设计理念和目前业界的“对象存储”较类似。
在TFS上线之前,淘宝网每个商品只允许上传一张图片,大
小限定在120KB之内,在商品详情中的图片必须使用外站的服
务。那时侯发布一件商品确实非常麻烦,笔者曾经想卖一台二
手电脑,我先把照片上传到Google相册,在发布到淘宝网之后发
现Google相册被墙(即被屏撇,无法访问),我的图片别人看不
54
54
淘宝技术这十年
到,当时很郁闷。在TFS上线后,商品展示图片开放到5张,商品
描述里面的图片也可以使用淘宝的图片服务,到现在为止,淘宝
网给每个用户提供了1GB的图片空间。技术和业务就是这么互相
借力推动着的,业务满足不了的时候,技术必须创新,技术创新
之后,业务有了更大的发展空间。
TFS发布之后,又经历了多个版本的修改,到1.3版的时候已
经比较成熟了。2009年6月,TFS 1.3版本上线,集群规模大大扩
展,部署到淘宝的图片生产系统上,整个系统已经从原有200台
PC服务器扩增至440台PC服务器(300B×12 SAS 15KB RPM)+
30台PC服务器(600B×12 SAS 15KB RPM )。支持文件数量也扩
容至百亿级别;系统部署存储容量为1800TB;当前实际存储容量
为995TB;单台DataServer支持随机IOPS900+,流量为15MB+;
55
55
第二部分:淘宝技术发展2
目前NameServer运行的物理内存是217MB(服务器使用千兆
网卡)。
TFS 1.3版本逻辑结构图如下图所示。
在TFS 1.3版本中,工程师们重点改善了心跳和同步的性能,
最新版本的心跳和同步在几秒钟之内就可完成切换,同时进行了
一些新的优化,包括元数据存储在内存中、清理磁盘空间等。
性能上也做了优化,包括如下内容。
yy 采用ext4文件系统,并且预分配文件,减少ext3等文件系统
数据碎片带来的性能损耗;
yy单进程管理单块磁盘的方式,摒除RAID5机制;
56
56
淘宝技术这十年
yy 带有HA机制的中央控制节点,在安全稳定和性能复杂度之
间取得平衡;
yy 缩减元数据大小,将更多的元数据加载入内存,提升访问
速度;
yy跨机架和IDC的负载均衡及冗余安全策略;
yy完全平滑扩容。
对于整个图片服务来说,仅有TFS还是不够的,整个图片服务
机器的拓扑结构如下图所示。
整个图片存储系统就像一个庞大的服务器,有处理单元、缓
存单元和存储单元。前面已经介绍过后台的TFS集群文件存储系
57
57
第二部分:淘宝技术发展2
统,在TFS前端,还部署着200多台图片文件服务器,用Apache实
现,用于生成缩略图的运算。
值得一提的是,根据淘宝网的缩略图生成规则,缩略图都是
实时生成的。这样做的好处有两点:一是为了避免后端图片服务
器上存储的图片数量过多,大大节约后台存储空间的需求,我们
计算过,采用实时生成缩略图的模式比提前全部生成好缩略图的
模式节约90%的存储空间。也就是说,存储空间只需要后一种模
式的10%。二是,缩略图可根据需要实时生成,这样更加灵活。
图片文件服务器的前端则是一级缓存和二级缓存,前面还有
全局负载均衡的设置,用于解决图片的访问热点问题。图片的
访问热点一定存在,重要的是,让图片尽量在缓存中命中。目前
淘宝网在各个运营商的中心点设有二级缓存,整体系统中心店设
有一级缓存,加上全局负载均衡,传递到后端TFS的流量就已经
非常均衡和分散了,对前端的响应性能也大大提高。根据淘宝的
缓存策略,大部分图片都尽量在缓存中命中,如果缓存中无法命
中,则会在本地服务器上查找是否存有原图,并根据原图生成缩
略图,如果都没有命中,则会考虑去后台TFS集群文件存储系统
上调取。因此,最终反馈到TFS集群文件存储系统上的流量已经
被大大优化了。
淘宝网将图片处理与缓存编写成基于Nginx的模块,我们认
为,Nginx是目前性能最高的HTTP服务器(用户空间),代码清
58
58
淘宝技术这十年
晰,模块化非常好。淘宝网使用GraphicsMagick进行图片处理,
采用了面向小对象的缓存文件系统,前端有LVS+Haproxy将原
图和其所有的缩略图请求都调度到同一台Image Server(图片服
务器)。
在文件定位上,内存用Hash算法做索引,最多一次读盘。另
外会有很多相同的图片重复上传上来,去除重复文件也是采用
Hash算法来做的。写盘方式则采用Append方式写,并采用了淘汰
策略FIFO,主要考虑降低硬盘的写操作,没有必要进一步提高
Cache命中率,因为ImageServer和TFS位于同一个数据中心,读盘
效率还是非常高的。
目前淘宝网的TFS已经开源(见code.taobao.org),业界的同
仁可以一起使用和完善这个系统。
Tair
TFS的开发让淘宝的图片功能得到了充分的发挥。同TFS一
样,很多技术都是在产品的推动下得到发展的。在介绍下面的技
术之前,有必要说说前些年我们做过的几个产品。
先说一个比较悲剧的——“团购”,这个团购可不是现在满
大街挂的那种Groupon类型的模式,在Groupon产生之前,在2006
年,淘宝的产品经理一灯就提出了“团购”这种产品。一灯最初
的设想是让买家在社区发起团购,“团长”找到足够的人之后,
59
59
第二部分:淘宝技术发展2
去跟卖家砍价,这类似于现在蘑菇街的“自由团”。但由于种种
原因,在开发的时候,对产品的功能做了裁剪,与最初的设想比
起来偏离了一点,变成了让卖家设置团购价,在买家达到指定的
数量之后,以团购价成交。这个功能看起来是结合了淘宝“一口
价”和“荷兰拍”的另一种交易模式,但最终没有支撑下去,这
种交易方式最大的弱点就是让买家看到了卖家的底牌,即便达不
到团购的数量,他们也往团购的价格上砍。当时为了提高流量,
淘宝网开辟了团购专区,实诚的卖家在达不到团购数量的时候,
被砍价砍亏了,狡猾的卖家干脆提高原价,利用这个专区做促
销。在接下来的两年里,这个产品沦落成了促销工具(话说现在
满大街的团购,其实也就是促销)。这个产品让研发人员对“产
品”这个概念有了深刻的认识。
再说一个更加悲剧的——“我的淘宝”。“我的淘宝”是给
会员管理自己的商品、交易、收货地址、评价、投诉的地方,这
个地方必须在登录之后才能看到,所以风格与外观完全不一样,
很长时间都没有优化过,样子丑,用户操作也不方便,如果一个
人有很多商品,上下架需要一个一个地操作,非常麻烦(想想那
些卖书的)。这时候一个重要人物承志(现在的蘑菇街CEO)
登场了,他给我们演示了最牛的前端交互技术,就是Gmail上那
种AJAX的交互方式,可以拖动,可以用鼠标右键,也可以用组
合键,操作完毕还不刷新页面,管理商品如有神助,帅呆了。我
是这个项目的项目经理,一灯是产品经理,我们再拉上万剑和一
60
60
淘宝技术这十年
伙工程师就开始行动了。我们热火朝天地干了三个月,快要完成
的时候,老马突然出现在我身后,看我操作了一遍新版“我的淘
宝”之后,问我这是不是客户端软件,我说是网页,他抓狂了,
说这跟客户端软件一样,链接下面的下画线都没有,上下架用文
件夹表示,他都不知道怎么操作,卖家肯定也不会玩。
页面如下图所示:看看这神乎其技的翻页条、精致的文件夹
结构、人性化的多选框,还有一个类似Excel的冻结窗口的功能,
这有多么性感啊!
61
61
第二部分:淘宝技术发展2
老马果然是神一样的人物,他说的应验了,淘宝历史上第一
个群体性事件爆发了,试用完新版本的“我的淘宝”之后,很多
卖家愤怒了,说不会玩儿。一灯就和承志一起商量怎么把页面改
得像网页一点,改了半个月,愤怒依然没有平息。我很无奈地看
着这两个人在那里坚持,然后跟老板们商量怎么办。后来我们到
论坛上让大家投票要不要使用新版“我的淘宝”,投票结果是一
半以上的人反对。于是这十来个人做了3个月的系统被杀掉了。
这让我非常沮丧,但最痛苦的还不是这个,我们下线之后,另外
一拨卖家不满了,说这么好的功能怎么没有了?这个产品带给我
们的是新技术(AJAX、prototype框架)的尝试,以及新技术对
用户操作习惯的改变,一定要慎之又慎。另外,还有一点没有总
结好的教训,就是应对群体事件的时候,我们手足无措,在后来
“招财进宝”和淘宝商城出现群体性事件的时候,我发现悲剧在
重演。
62
62
淘宝技术这十年
说到“ 招财进宝” , 这个是最悲剧的产品。在2 0 0 6 年
“五一”的时候,一个划时代的项目启动了。财神说要用最好的
项目阵容,我被选中了,这一下让我觉得我能划分到最好的员工
之类,在“我的淘宝”这个产品中严重受伤的心又痊愈了。这是
一个商品P4P的系统,就是按成交付费。我们认为已经有很多卖家
有钱了,但淘宝上这么多的商品,他们很难被找到,卖家愿意花
钱让商品排在前面。我们允许卖家购买广告位,把他的商品按一
定算法给出排名(类似于百度的竞价排名,但不仅仅看他出了多
少钱,还要看信用、成交量、被收藏数量等,这个算法弄得巨复
杂)。这是一个多么牛的盈利模式啊!
这个系统进行得很顺利,但发布的时候,更大的群体性事件
出来了,买家们质疑:你们不是承诺三年不收费吗?收广告费不
是收费吗?后来我们的竞争对手又推波助澜,公关公司和圈子里
各路大侠上蹿下跳,甚至同行推出了“一键搬家”的功能来收纳
我们的会员。一时间,舆论哗然,各种矛头都指了过来。为了收
场,我们又一次在论坛中让用户投票决定产品是否下线,同“我
的淘宝”一样,以悲剧收场。也如同“我的淘宝”一样,下线
后,一拨尝到甜头的卖家说,这么好的功能怎么没有了?(直到
Yahoo中国合并之后,开发了淘宝直通车,才以类似的产品形态
满足了这部分需求。)
虽然“招财进宝”失败了,但这个项目中对技术的探索更加
深入,其中用到了用户行为追踪、AJAX等,而且有一个技术的细
63
63
第二部分:淘宝技术发展2
节非常经典,淘宝商品详情页面每天的流量有几个亿,里面的内
容都是放在缓存里的,做“招财进宝”的时候,我们要给卖家显
示他的商品被浏览的次数(见下图),这个数字必须实时更新,
而用缓存一般都是异步更新的,所以,一开始根本没考虑把这个
数据放入缓存里。我们的商品表里增加了这样一个字段,每增加
一个PV,该字段就要更新一次。发布一个小时后,数据库就挂
掉了。数据库撑不住怎么办?一般的缓存策略是不支持实时更新
的,这时候多隆大神想了个办法,在Pache上面写了一个模块,这
个数字根本不经过下层的WebApp容器(只经过Apache)就写入一
个集中式的缓存区了,这个缓存区的数据再异步更新到数据库。
这就是我前面提到的,整个商品详情的页面都在缓存中了,把缓
存用到了极致。
接下来,我们就说说缓存的技术吧。
淘宝在很早就开始使用缓存技术了,在2004年的时候,我们
使用一个叫做ESI(Edge Side Includes)的缓存(Cache)。在决
定采用ESI之前,多隆试用了Java的很多Cache,但都比较重,后
来用了Oracle Web Cache,也经常挂掉,Oracle Web Cache也支持
64
64
淘宝技术这十年
ESI,多隆由此发现了ESI这个好东东。ESI是一种数据缓冲/缓存
服务器,它提供将Web网页的部分(这里指页面的片段)进行缓
冲/缓存的技术及服务。以往的数据缓冲服务器和信息传送服务以
“页”为单位制作,复制到数据缓冲服务器中,这用于处理静态
页面很有效,但在面对动态内容的时候,就很难得到高效率。在
ESI中是部分的缓冲网页,使用基于XML的标记语言,指定想要
缓冲的页面部分。由此,页面内分为动态地变更部分和静态的不
变更部分,只将静态的部分有效地发送到服务器中。淘宝网的数
据虽然大部分都是动态产生的,但页面中的静态片段也有很多,
例如,页面的头、尾,商品详情页面的卖家信息等(如下图右
侧),这些最早都是从ESI缓存中读取的。
ESI解决了页面端静态片段的缓存,聪明的读者可能马上就想
到了,在后端的那些数据能不能使用缓存?显然也是可以的,而
65
65
第二部分:淘宝技术发展2
且是必需的。例如,一个大卖家的商品和店铺,一天的浏览量可
能是几百万个,一个小卖家的可能只有几个,那么这个大卖家的
用户信息要是每次都从数据库中读取,显然不划算,要是把这个
信息放在内存中,每次都从内存里取,性能要好很多。这种应用
场景就是memcached这种Key-Value缓存的用武之地。只可惜,在
淘宝急需要memcached的时候,它还没有崭露头角(它于2003年6
月出现,但近几年才火爆起来,当时没发现它)。我们的架构师
多隆大神再一次出手写了一个缓存系统,叫TBstore,这是一个分
布式的基于Berkeley DB的缓存系统,推出之后,在阿里巴巴集团
内部使用非常广泛,特别是对于淘宝,TBstore上应用了ESI(就是
上面说过的那个ESI)、Checkcode(验证码)、Description(前文
说过的商品详情)、Story(心情故事,商品信息里面的一个大字
段,长度仅次于商品详情)、用户信息等内容。
TBstore的分布式算法实现:根据保存的Key(关键字),对
key进行Hash算法,取得Hash值,再对Hash值与总Cache服务器数
据取模。然后根据取模后的值,找到服务器列表中下标为此值的
Cache服务器。由Java Client API封装实现,应用无须关心。
TBstore有一个优点,这也是它的弱点,它的存储是基于
Berkeley DB的,而Berkeley DB在数据量超过内存的时候,就要往
磁盘上写数据了,所以,它是可以做持久化存储的。但是一旦往
磁盘写入数据,作为缓存的性能就大幅下降。
66
66
淘宝技术这十年
这时又有一个项目,推动了淘宝在缓存方面的技术提升。在
2007年,我们把淘宝的用户信息独立出来,形成一个中心系统
UIC(User Information Center),因为淘宝所有的功能都要依赖
于用户信息,所以这个模块必须单独拿出来,否则以后的系统无
法扩展。把UIC拿出来以后,应用系统访问UIC,UIC访问数据
库取得用户信息,粗算一下,每天要取几十亿条的用户信息,若
直接查询数据库,数据库肯定会崩溃,这里必须要用缓存。于是
多隆专门为UIC写了一个缓存系统,取名叫做TDBM。TDBM抛
弃了Berkeley DB的持久功能,数据全部存放在内存中。到2009
年,多隆又参考了memcached的内存结构,改进了TDBM的集群
分布方式,在内存利用率和吞吐量方面又做了大幅提升,推出了
TDBM 2.0系统。
下图是一个关键应用的实时监控信息,第一行是UIC的缓存
命中率,可以看到有99.2%之高。换句话说,也就是给数据库减少
了99.2%的压力。
67
67
第二部分:淘宝技术发展2
由于TDBM、TBstore的数据接口和用途都很相似,开发团
队把二者合并,推出了淘宝自创的Key-Value缓存系统——Tair
(TaoBao Pair的意思,Pair即Key-Value数据对)。Tair包括缓
存和持久化两种存储功能。Tair作为一个分布式系统,由一个中
心控制节点和一系列的服务节点组成,我们称中心控制节点为
Config Server,服务节点是Data Server。Config Server 负责管理所
有的Data Server,维护Data Server的状态信息。Data Server 对外
提供各种数据服务,并以心跳的形式将自身的状况汇报给Config
Server。Config Server是控制点,而且是单点,目前采用一主一备
的形式来保证其可靠性。所有的Data Server 地位都是等价的。Tair
的架构图如下图所示。
68
68
淘宝技术这十年
系统部署结构如下图所示。
目前,Tair支撑了淘宝几乎所有系统的缓存信息。Tair已开
源,地址为code.taobao.org。
在创造了TFS和Tair之后,整个系统的架构如下图所示。
69
69
第二部分:淘宝技术发展2
在这个时候,研发部对搜索引擎iSearch也进行了一次升级,
之前的搜索引擎是把数据分到多台机器上,但是每份数据只有一
份,现在是每份数据变成多份,整个系统从一个单行的部署变成
了矩阵,能够支撑更大的访问量,并且做到很高的可用性。
到2007年,淘宝网日均PV达到2.5亿个,商品数超过1亿个,
注册会员数达5千多万个,全网成交额达433亿元。
第三部分
淘宝技术发展3
“所有的进步都是不稳定的,一个问题解决了,我们不得不面对又一个新问
题。”
——马丁·路德·金
72
72
淘宝技术这十年
分布式时代
服务化
在系统发展的过程中,架构师的眼光至关重要,作为程序
员,只要把功能实现即可,但作为架构师,要考虑系统的扩展
性、重用性,对于这种敏锐的感觉,有人说是一种“代码洁
癖”。淘宝早期有几个架构师具备了这种感觉,周悦虹开发的
Webx是一个扩展性很强的框架,行癫在这个框架上插入了数据
分库路由的模块、Session框架等。在做淘宝后台系统的时候,同
样需要这几个模块,行癫指导我把这些模块单独打成JAR包。另
外,在做淘宝机票、彩票系统的时候,页面端也有很多东西需要
复用,最直观的是页眉和页脚,一开始,我们的每个系统中都复
制了一份,但奇妙的是,那段时间页脚要经常修改,例如,把
“雅虎中国”改成“中国雅虎”,过一段时间又加了一个“口碑
网”,再过一段时间变成了“雅虎口碑”,最后又变成了“中国
雅虎”。后来我就把这部分Velocity模板独立出来做成了公用的模
块。
上面说的都是比较小的复用模块,到2006年,我们做了一个
73
73
第三部分:淘宝技术发展3
商品类目属性的改造,在类目中引入了属性的概念。项目的代
号叫做“泰山”,如同它的名字一样,这是一个举足轻重的项
目,这个改变是一个划时代的创新。在这之前的三年时间内,商
品的分类都是按照树状一级一级的节点来分的,随着商品数量的
增长,类目也变得越来越深,且越来越复杂,这样,买家如果要
查找一件商品,就要逐级打开类目,找商品之前要弄清商品的分
类。而淘宝运营部门管理类目的小二也发现了一个很严重的问
题,例如,男装里有T恤、T恤下面有耐克、耐克有纯棉的,女装
里也有T恤、T恤下面还是有耐克、耐克下面依然有纯棉的,那是
先分男女装,再分款式、品牌和材质呢,还是先分品牌,再分款
式、材质和男女装呢?弄得很乱。这时候,一位大侠出来了——
一灯,他说品牌、款式、材质等都可以叫做“属性”,属性是类
似Tag(标签)的一个概念,与类目相比更加离散、灵活,这样
也缩减了类目的深度。这个思想的提出一举解决了分类的难题!
从系统的角度来看,我们建立了“属性”这样一个数据结构,由
于除了类目的子节点有属性外,父节点也可能有属性,于是类目
属性合起来也是一个结构化的数据对象。这个做出来之后,我们
把它独立出来作为一个服务,叫做Catserver(Category Server)。
跟类目属性密切关联的商品搜索功能独立出来,叫做Hesper(金
星)。Catserver和Hesper供淘宝的前后台系统调用。
现在淘宝的商品类目属性已经是全球最大的,几乎没有什么
类目的商品在淘宝上找不到(除了违禁的),但最初的类目属性
74
74
淘宝技术这十年
改造完之后,我们很缺乏属性数据,尤其是数码类。从哪里弄这
些数据呢?我们跟“中关村在线”合作,拿到了很多数据,那个
时候,很多商品属性信息的后边标注着:“来自中关村在线”。
有了类目属性,给运营工作带来了很大的便利,我们知道淘宝
的运营主要就是类目的运营,什么季节推什么商品,都要在类目
属性上做调整,让买家更容易找到。例如,夏天让用户在女装一
级类目下标出材质是不是蕾丝的、是不是纯棉的,冬天却要把羽
绒衣调到女装一级类目下,什么流行,就要把什么商品往更高级
的类目调整。这样类目和属性要经常调整,随之而来的问题就出
现了——调整到哪个类目,所属商品的卖家就要编辑一次自己的
商品,随着商品量的增长,卖家的工作量越来越大,他们肯定受
不了。到了2008年,我们研究了超市里前后台商品的分类,发现
超市前台商品可以随季节和关联来调整摆放场景(例如著名的啤
酒和尿布的关联),后台仓库里要按照自然类目来存储,二者密
切关联,却又相互分开这样卖家发布商品选择的是自然类目和属
性,淘宝前台展示的是根据运营需要摆放商品的类目和属性。
改造后的类目属性服务取名为Forest(森林,与类目属性有点神
似。Catserver还用于提供卖家授权、品牌服务、关键词等相关的
服务)。类目属性的服务化是淘宝在系统服务化方面做的第一个
探索。
虽然个别架构师具备了“代码洁癖”,但淘宝前台系统的业
75
75
第三部分:淘宝技术发展3
务量和代码量还是呈爆炸式的增长。业务方总在后面催,开发
人员不够就继续招人,招来的人根本看不懂原来的业务,只好摸
索着在“合适的地方”加一些“合适的代码”,看看运行起来像
那么回事后,就发布上线。在这样的恶性循环中,系统越来越臃
肿,业务的耦合性越来越高,开发的效率越来越低。借用当时比
较流行的一句话“你写一段代码,编译一下能通过,半个小时就
过去了;编译一下没通过,半天就过去了。”在这种情况下,系
统出错的概率也逐步增长,常常是你改了商品相关的某些代码,
发现交易出问题了,甚至你改了论坛上的某些代码,旺旺出问
题了。这让开发人员苦不堪言,而业务方还认为开发人员办事
不力。
大概是在2007年年底的时候,研发部空降了一位从硅谷来的
高管——空闻大师。空闻是一位温厚的长者,他告诉我们一切要
以稳定为中心,所有影响系统稳定的因素都要解决掉。例如,每
做一个日常修改,都必须对整个系统回归测试一遍;多个日常修
改如果放在一个版本中,要是一个功能没有测试通过,整个系统
都不能发布。我们把这个叫做“火车模型”,即任何一个乘客没
有上车,都不许发车。这样做最直接的后果就是火车一直晚点,
新功能上线更慢了,我们能明显感觉到业务方的不满,空闻的压
力肯定非常大。
现在回过头来看看,其实我们并没有理解背后的思路。正是
76
76
淘宝技术这十年
在这种要求下,我们不得不开始改变一些东西,例如,把回归测
试日常化,每天晚上都跑一遍整个系统的回归。另外,在这种要
求下,我们不得不对这个超级复杂的系统做肢解和重构,其中复
用性最高的一个模块——用户信息模块开始拆分出来,我们叫它
UIC(User Information Center)。在UIC中,它只处理最基础的用
户信息操作,例如,getUserById、getUserByName等。
在另一方面,还有两个新兴的业务对系统基础功能的拆分也
提出了要求。在那个时候,我们做了淘宝旅行(trip.taobao.com)
和淘宝彩票(caipiao.taobao.com)两个新业务,这两个新业务在商
品的展示和交易的流程上都跟主站的业务不一样,机票是按照航
班的信息展示的,彩票是按照双色球、数字和足球的赛程来展示
的。但用到的会员功能和交易功能是与主站差不多的,当时做起
来就很纠结,因为如果在主站中做,会有一大半跟主站无关的东
西,如果重新做一个,会有很多重复建设。最终我们决定不再给
主站添乱了,就另起炉灶做了两个新的业务系统,从查询商品、
购买商品、评价反馈、查看订单这一整个流程都重新写了一套。
现在在“我的淘宝”中查看交易记录的时候,还能发现“已买到
的宝贝”中把机票和彩票另外列出来了,他们没有加入到普通的
订单中。在当时,如果已经把会员、交易、商品、评价这些模块
拆分出来,就不用什么都重做一遍了。
77
77
第三部分:淘宝技术发展3
到2008年初,整个主站系统(有了机票、彩票系统之后,把
原来的系统叫做主站)的容量已经到了瓶颈,商品数在1亿个以
上,PV在2.5亿个以上,会员数超过了5000万个。这时Oracle的
连接池数量都不够用了,数据库的容量到了极限,即使上层系
统加机器也无法继续扩容,我们只有把底层的基础服务继续拆
分,从底层开始扩容,上层才能扩展,这才能容纳以后三五年的
增长。
于是我们专门启动了一个更大的项目,即把交易这个核心业
务模块拆分出来。原来的淘宝交易除了跟商品管理耦合在一起,
还在支付宝和淘宝之间转换,跟支付宝耦合在一起,这会导致系
统很复杂,用户体验也很不好。我们把交易的底层业务拆分出
78
78
淘宝技术这十年
来,叫交易中心(Trade Center,TC),所谓底层业务,就如创
建订单、减库存、修改订单状态等原子型的操作;交易的上层业
务叫交易管理(Trade Manager,TM),例如,拍下一件普通商
品要对订单、库存、物流进行操作,拍下虚拟商品不需要对物流
进行操作,这些在TM中完成。这个项目取了一个很没有创意的名
字——“千岛湖”,开发人员取这个名字的目的是想在开发完毕
之后,去千岛湖玩一圈,后来他们如愿以偿了。这个时候还有一
个淘宝商城的项目在做,之前拆分出来的那些基础服务给商城的
快速构建提供了良好的基础。
79
79
第三部分:淘宝技术发展3
类目属性、用户中心、交易中心,随着这些模块的逐步拆分
和服务化改造,我们在系统架构方面也积累了不少经验。到2008
年年底就做了一个更大的项目,把淘宝所有的业务都模块化,这
是继2004年从LAMP架构到Java架构之后的第二次脱胎换骨。我们
对这个项目取了一个很霸气的名字——“五彩石”(女娲炼石补
天用的石头)。这个系统重构的工作非常惊险,有人称为“给一
架高速飞行的飞机换发动机”。“五彩石”项目发布之后,相关
工程师去海南三亚玩了几天。
80
80
淘宝技术这十年
他们把淘宝的系统拆分成了如下架构。
其中,UIC和Forest在上文已说过,TC、IC、SC分别是交
易中心(Trade Center)、商品中心(Item Center)、店铺中心
(Shop Center),这些中心级别的服务只提供原子级的业务逻
辑,如根据ID查找商品、创建交易、减少库存等操作。再往上
一层是业务系统TM(Trade Manager,交易业务)、IM(Item
Manager,商品业务)、SM(Shop Manager,后来改名叫SS,即
Shop System,店铺业务)、Detail(商品详情)。
拆分之后,系统之间的交互关系变得非常复杂,示意图如下
所示。
81
81
第三部分:淘宝技术发展3
系统这么拆分的好处显而易见,拆分之后的每个系统可以单
独部署,业务简单,方便扩容;有大量可重用的模块便于开发
新的业务;能够做到专人专事,让技术人员更加专注于某一个领
域。这样要解决的问题也很明显,分拆之后,系统之间还是必须
要打交道的,越往底层的系统,调用它的客户越多,这就要求底
层的系统必须具有超大规模的容量和非常高的可用性。另外,拆
分之后的系统如何通信?这里需要两种中间件系统,一种是实时
调用的中间件(淘宝的HSF,高性能服务框架),一种是异步消
息通知的中间件(淘宝的Notify)。另外,一个需要解决的问题
是用户在A系统登录后,到B系统的时候,用户的登录信息怎么保
存?这又涉及一个Session框架。再者,还有一个软件工程方面的
问题,这么多层的一套系统,怎么去测试它?
82
82
淘宝技术这十年
中间件
HSF
互联网系统的发展看似非常专业,其实在生活中也存在类似
的“系统”,正如一位哲学家说“太阳底下无新事”。我们可以
从生活中的一个小例子来看网站系统的发展,这个例子是HSF的
作者毕玄写的。
一家小超市,一个收银员,同时还兼着干点其他的事情,例
如,打扫卫生、摆货。
来买东西的人多起来了,排队很长,顾客受不了,于是增加
了一个收银台,雇了一个收银员。
忙的时候收银员根本没时间去打扫卫生,超市内有点脏,于
是雇了一个专门打扫卫生的。
随着顾客不断增加,超市也经过好几次装修,由以前的一层
变成了两层,这个时候所做的事情就是不断增加收银台、收银员
和打扫卫生的人。
在超市运转的过程中,老板发现一个现象,有些收银台排很
长的队,有些收银台排的人不多,了解后知道是因为收银台太多
了,顾客根本看不到现在各个收银台的状况。对于这个现象,一
种简单的方法就是继续加收银台。但一方面,超市没地方可加收
83
83
第三部分:淘宝技术发展3
银台了,另一方面,作为老板,当然不需要雇太多的人,于是开
始研究怎样让顾客了解到收银台的状况,简单地加了一个摄像头
和一个大屏幕,在大屏幕上显示目前收银台的状况,这样基本解
决了这个问题。
排队长度差不多后,又出现了一个现象,就是有些收银台速
度明显比其他的慢,原因是排在这些收银台的顾客买的东西特别
多,于是又想了一招,就是设立专门的10件以下的通道,这样买
东西比较少的顾客就不用排太长的队了,这一招施展后,顾客的
满意度明显提升,销售额也好了不少,后来就继续用这招应对团
购状况、VIP 状况。
在解决了上面的一些烦心事后,老板关注到了一个存在已
久的现象,就是白天收银台很闲,晚上则很忙,于是从节省成
本上考虑,决定实行部分员工只在晚上上班的机制,白天则关
闭一些收银台,顾客仍然可以通过大屏幕看到哪些收银台是关
闭的,避免走到没人的收银台去,实行这招后,成本大大降
低了。
这个生活中的例子及其解决的方法,其实和互联网网站发展
过程中的一些技术是非常类似的,只是在技术层面用其他名词来
表达了而已,例如,有集群、分工、负载均衡、根据QoS分配资
源等。
84
84
淘宝技术这十年
yy集群:所有的收银员提供的都是收银功能,无论顾客到哪
一个收银员面前,都可完成付款,可以认为所有的收银员
就构成了一个集群,都希望能做到顾客增加的时候只需增
加收银员就行。在现实生活中有场地的限制,而在互联网
应用中,能否集群化还受限于应用在水平伸缩上的支撑程
度,而集群的规模通常会受限于调度、数据库、机房等。
yy分工:收银员和打扫卫生的人分开,这种分工容易解决,
而这种分工在互联网中是一项重要而复杂的技术,没有现
实生活中这么简单,涉及的主要有按功能和数据库的不同
拆分系统等,如何拆分以及拆分后如何交互是需要面临的
两个挑战。因此,会有高性能通信框架、SOA平台、消息
中间件、分布式数据层等基础产品的诞生。
yy负载均衡:让每个收银台排队差不多长,设立小件通道、
团购通道、VIP通道等,这些可以认为都是集群带来的负载
均衡的问题,从技术层面上说,实现起来自然比生活中复
杂很多。
yy根据QoS分配资源:部分员工仅在晚上上班的机制要在现实
生活中做到不难,而对互联网应用而言,就是一件复杂而
且极具挑战的事。
参照生活中的例子来说,在面对用户增长的情况下,想出这
85
85
第三部分:淘宝技术发展3
些招应该不难,不过要掌握以上四点涉及的技术就相当复杂了,
而且互联网中涉及的其他很多技术还没在这个例子中展现出来,
例如缓存、CDN等优化手段;运转状况监测、功能降级、资源劣
化、流控等可用性手段,自建机房、硬件组装等成本控制手段。
因此,构建一个互联网网站确实是不容易的,技术含量十足,当
然,经营一家超市也不简单。
从超市的运维可以抽象出系统设计的一些思路,服务拆分之
后,如何取得我需要的服务?在“电视机”上,把每个集群能提
供的服务显示出来。你不需要关心哪个人为你服务,当你有需要
的时候,请先看头顶的电视机,它告诉你哪个服务在哪个区域。
当你直接去这个区域的时候,系统会给你找到一个最快速的服务
通道。
这就是HSF的设计思想,服务的提供者启动时通过HSF框架
向ConfigServer(类似超市的电视机)注册服务信息(接口、版
本、超时时间、序列化方式等),这样ConfigServer上面就定义
了所有可供调用的服务(同一个服务也可能有不同的版本);服
86
86
淘宝技术这十年
务调用者启动的时候向ConfigServer注册对哪些服务感兴趣(接
口、版本),当服务提供者的信息变化时,ConfigServer向相应
的感兴趣的服务调用者推送新的服务信息列表;调用者在调用时
则根据服务信息的列表直接访问相应的服务提供者,而无须经过
ConfigServer。我们注意到ConfigServer并不会把服务提供者的IP地
址推送给服务的调用者,HSF框架会根据负载状况来选择具体的
服务器,返回结果给调用者,这不仅统一了服务调用的方式,也
实现了“软负载均衡”。平时ConfigServer通过和服务提供者的心
跳来感应服务提供者的存活状态。
在HSF的支持下,服务集群对调用者来说是“统一”的,服
务之间是“隔离”的,这保证了服务的扩展性和应用的统一性。
再加上HSF本身能提供的“软负载均衡”,服务层对应用层来说
就是一片“私有云”了。
87
87
第三部分:淘宝技术发展3
HSF框架以SAR包的方式部署到Jboss、Jetty或Tomcat下,在应
用启动的时候,HSF(High-Speed Service Framework,在开发团
队内部有一些人称HSF为“好舒服”)服务随之启动。HSF旨在为
淘宝的应用提供一个分布式的服务框架,HSF从分布式应用层面
以及统一的发布/调用方式层面为大家提供支持,从而可以很容易
地开发分布式的应用以及提供或使用公用功能模块,而不用考虑
分布式领域中的各种细节技术,例如,远程通讯、性能损耗、调
用的透明化、同步/异步调用方式的实现等问题。
从上图HSF的标志来看,它的速度是很快的。HSF是一个分
布式的标准Service方式的RPC(Remote Procedure Call Protocol,
远程过程调用协议)框架,Service的定义基于OSGI的方式,通
讯层采用TCP/IP协议。关于分布式的服务框架的理论基础,HSF
的作者毕玄写了一篇博文(http://www.blogjava.net/BlueDavy/
archive/2008/01/24/177533.html),有关基于OSGI的分布式服
务框架,也有一系列的博文(http://www.blogjava.net/BlueDavy/
archive/2008/01/14/175054.html)。
从下面这个HSF监控系统的截图中可以更直观地看到一些信
88
88
淘宝技术这十年
息,在两个集群中有两个服务器(其实有更多的,没有全部截
图下来)都提供com.taobao.item.service.SpuGroupService 这一服
务,版本号都是1.0.0,这个服务在ConfigServer上的注册信息中
包含超时时间、序列化方式。在后面那条信息中可看到,在展开
的这个集群中服务有835台机器已订阅,这些订阅者有淘宝的服
务器(cart是购物车功能的服务器),也有hitao(淘花网)的服
务器。
HSF系统目前每天承担了300亿次以上的服务调用。
一些读者可能会有一个疑问:既然淘宝的服务化是渐进式
的,那么在HSF出现之前,系统之间的调用采用什么方式呢?
这个有点“五花八门”,例如,对于类目的调用方式是:
Forest打包成一个JAR包,在应用启动的时候装载到内存中,仅
这一个JAR包所占用的内存就有800MB之多(因为淘宝的类目数
据太庞大了),对于当时一般只有2GB内存的开发机来说,加载
完类目信息后,机器运行速度就非常慢。对于用户信息(UIC)
89
89
第三部分:淘宝技术发展3
来说,一开始的调用方式是用Hessian接口。还有一些系统是通过
WebService、Socket甚至是HTTP请求来相互调用的。每种调用方
式都涉及各种超时、信息的加解/密、参数的定义等问题,由此可
见,在没有HSF之前,系统之间的调用是错综复杂的。而随着系
统拆分得越来越多,必须由一个统一的中间层来处理这种问题,
HSF正是在这种背景下诞生的。
Notify
HSF解决了服务调用的问题,我们再提出一个很早就说过的
问题:用户在银行的网关付钱后,银行需要通知到支付宝,但银
行的系统不一定能发出通知;如果通知发出了,不一定能通知
到;如果通知到了,不一定不重复通知一遍。这个状况在支付宝
持续了很长时间,非常痛苦。支付宝从淘宝剥离出来的时候,淘
宝和支付宝之间的通信也面临同样的问题,那是2005年的事情,
支付宝的架构师鲁肃提出用MQ(Message Queue)的方式来解决
这个问题,我负责淘宝这边读取消息的模块。但我们发现消息数
量上来之后,常常造成拥堵,消息的顺序也会出错,在系统挂掉
的时候,消息也会丢掉,这样非常不保险。然后鲁肃提出做一个
系统框架上的解决方案,把要发出的通知存放到数据库中,如果
实时发送失败,再用一个时间程序来周期性地发送这些通知,系
统记录下消息的中间状态和时间戳,这样保证消息一定能发出,
也一定能通知到,且通知带有时间顺序,这些通知甚至可以实现
事务性的操作。
90
90
淘宝技术这十年
在“千岛湖”项目和“五彩石”项目之后,淘宝自家的系统
也拆成了很多个,他们之间也需要类似的通知。例如,拍下一
件商品,在交易管理系统中完成时,它需要通知商品管理系统减
少库存,通知旺旺服务系统发送旺旺提醒,通知物流系统上门取
货,通知SNS系统分享订单,通知公安局的系统这是骗子……用
户的一次请求,在底层系统可能产生10次的消息通知。这一大堆
的通知信息是异步调用的(如果同步,系统耦合在一起就达不到
拆分的目的),这些消息通知需要一个强大的系统提供支持,从
消息的数量级上看,比支付宝和淘宝之间的消息量又上了一个层
次,于是按照类似的思路,一个更加强大的消息中间件系统就诞
生了,它的名字叫做Notify。Notify是一个分布式的消息中间件系
统,支持消息的订阅、发送和消费,其架构图如下所示。
91
91
第三部分:淘宝技术发展3
NotifyServer在ConfigServer上面注册消息服务,消息的客户端
通过ConfigServer订阅消息服务。某个客户端调用NotifyServer发
送一条消息,NotifyServer负责把消息发送到所有订阅这个消息的
客户端(这个过程参照HSF一节,原理是一样的)。为了保证消
息一定能发出,且对方也一定能收到,消息数据本身就需要记录
下来,这些信息存放在数据库中(可以是各种数据库)。由于消
息具有中间状态(已发送、未发送等),应用系统通过Notify可
以实现分布式事物——BASE(基本可用(Basically Available)、
软状态 (Soft State)、最终一致(Eventually Consistent))。
NotifyServer可以水平扩展,NotifyClient也可以水平扩展,数据
库也可以水平扩展,从理论上讲,这个消息系统的吞吐量是没
有上限的,现在Notify系统每天承载了淘宝10亿次以上的消息
通知。
下图展示了创建一笔交易之后,TC(交易中心)向Notify发
送一条消息,后续Notify所完成的一系列消息通知。
TDDL
有了HSF和Notify的支持,在应用级别中,整个淘宝网的系统
可以拆分了,还有一个制约系统规模的更重要的因素,就是数据
库,也必须拆分。
92
92
淘宝技术这十年
在第二部分中讲过,淘宝很早就对数据进行过分库的处理,
上层系统连接多个数据库,中间有一个叫做DBRoute的路由来对
数据进行统一访问。DBRoute对数据进行多库的操作、数据的整
合,让上层系统像操作一个数据库一样操作多个库。但是随着数
据量的增长,对于库表的分法有了更高的要求,例如,你的商品
数据到了百亿级别的时候,任何一个库都无法存放了,于是分成
2个、4个、8个、16个、32个……直到1024个、2048个。好,分成
这么多,数据能够存放了,那怎么查询它?这时候,数据查询的
中间件就要能够承担这个重任了,它对上层来说,必须像查询一
93
93
第三部分:淘宝技术发展3
个数据库一样来查询数据,还要像查询一个数据库一样快(每条
查询在几毫秒内完成),TDDL就承担了这样一个工作。
另外,加上数据的备份、复制、主备切换等功能,这一套系
统都在TDDL中完成。在外面有些系统也用DAL(数据访问层)
这个概念来命名这个中间件。
TDDL实现了下面三个主要的特性:
yy数据访问路由——将针对数据的读写请求发送到最合适的
地方;
yy数据的多向非对称复制——一次写入,多点读取;
yy数据存储的自由扩展——不再受限于单台机器的容量瓶颈
与速度瓶颈,平滑迁移。
下图展示了TDDL所处的位置。
94
94
淘宝技术这十年
下图展示了一个简单的分库分表数据查询策略。
下面是TDDL的主要开发者之一沈询讲述的“TDDL的前世今
生”——数据层的发展历程。
CommonDAO的时代
数据切分并不算是一个很新的概念,当商品库切分为两个
时,就已经出现了名字叫做xingdian(笑,那时候行癫已经不写
代码了,但从代码的版本信息可以看到作者)的人写的Common
DAO。
CommonDAO的思路非常简单实用,因为淘宝主要在使用
ibatis作为访问数据库的DAO层,所以,CommonDAO的作用就是
对ibatis层做了一个很浅的封装,允许你通过商品字串ID的第一个
95
95
第三部分:淘宝技术发展3
字符来访问两台数据库中的一台。
比如,如果字符串ID的第一个字符是0~7,那么走到数据库1
去,如果是8~f,则走到数据库2去。同时,也允许用户直接给定
数据库的名字来访问数据库。
这应该是最早的数据层原型。
TDDL 1.0时代
后来,大家逐渐发现,如果按照业务的发展规模和速度,那
么使用高端存储和小型机的Oracle存储的成本将难以控制,于是
降低成本就成了必然。
如何能够在不影响业务正常发展的前提下,从一定程度上解
决成本的问题呢?
“对一部分数据库使用MySQL”,DBA们的决策是这样,于
是,分布式数据层的重担就落到了华黎的头上。
别看现在数据水平切分似乎已经成了基础知识。在2007年、
2008年,如何设计它还真是让我们伤透了脑筋。
当时的我们,只知道eBay有一个数据层,却不知道如何设计
和实现?
于是邀请了当时所有的业务负责人来畅想数据层的样子……