看了《淘宝技术这十年》,总结于此。
从无到有
2003年,eBay 和易趣在资本方面正打得不可开交,那怎么在最短的时间内把一个网站从零开始 建立起来呢?
淘宝为了尽快上线,仅花了一个月把买来的LAMP(Linux+ Apache+MySQL+PHP)改造上线。主要的改造是:从一个数据库进行所有的读写操作,拆分成一个主库、两个从库的结构,读写分离。
这么做的好处有几点:有了备份,使得安全性增加了,读写效率得以提升。随着用户需求和流量的不 断增长,系统做了很多日常改进,服务器由最初的一台变成了三 台,一台负责发送Email、一台负责运行数据库、一台负责运行 WebApp。一段时间之后,商品搜索的功能占用数据库资源太大了 (用like搜索的,很慢),2003年7月,多隆又把阿里巴巴中文站 的搜索引擎iSearch搬了过来。
数据库切换
在2003年年底,MySQL已经撑不住了,技术的替代方案非常简单,就是换成Oracle。换为Oracle的原因除了它容量大、稳定、 安全、性能高之外,还有人才方面的原因。在2003年的时候,Oracle给全球的技术专家颁发一些头衔,其中最高级别的叫ACE,被授予这个头衔当年全球只有十几名,而阿里巴巴就有4名。
Oracle的性能和并发访问能力之所以强大,有一个关键性的设计——连接池,连接池中放的是进程级别的长连接,要独占一部分内存空间。也就是说,这些连接数在固定内存的Oracle Server 上是有限的,任何一个请求只需要从连接池中取得一个连接即可,用完后释放,这不需要频繁地创建和断开连接,
但对于PHP语言来说,它对数据库的访问都是很直接的。每一个请求都要一个连接。如果是长连 接,应用服务器增多时连接数就多了,就会把数据库拖挂;如果是短连接,频繁地连接后再断开,性能会非常差(而Java语言有很多现成的连接池)。为了解决这个问题,引入了一个开源的连接池代理服务SQL Relay,提供连接池的功能,多隆对它进行了一些功能改进之后,系统的架构就变成了如下形式。
后来数据量变大后,本地存储无法 满足了,买了NAS(Network Attached Storage,网络附属存储), NetApp(Network Appliance,美国网域存储技术有限公司)的 NAS作为数据库的存储设备,加上Oracle RAC来实现负载均衡。后来采购了Dell和EMC合作的SAN低端存储,性能一下提升了十几倍,这才比较稳定了。再后来,数据 量更大了,存储的节点一拆二、二拆四,RAC又出问题了,这才 踏上了购买小型机的道路。在那段不稳定的时间里,替换完数据库后,时间到了2004年春天,他们在把数据的连接放 在SQL Relay之后就噩梦不断,这个代理服务经常会死锁,如同之 前的MySQL死锁一样。虽然多隆做了很多修改,问题很多,最快的解决办法就是“重启” 它的服务。这在白天还好,只要连接上机房的服务器,把进程杀 掉,然后开启就可以了。但是最痛苦的是它在晚上也要死掉,于 是工程师们不得不24小时开着手机,一旦收到“SQL Relay进程挂 起”的短信,打开电脑连上机房的网络重 启服务。
2004年初, SQL Relay的问题解决不了,数据库必须要用Oracle,只有换开发语言了。
Java是当时 最成熟的网站开发语言,它有比较良好的企业开发框架,被世界 上主流的大规模网站普遍采用。现在摆在他 们面前的问题是用什么办法把一个庞大的网站从PHP语言迁移到 Java?
他们的大致方案是 给业务分模块,一个模块一个模块地渐进式替换。如用户模块, 老的member.taobao.com继续维护,不添加新功能,新功能在新 的模块上开发,跟老的模块共用一个数据库,开发完毕之后放到 不同的应用集群上,另开一个域名member1.taobao.com,同时再 替换老的功能,替换一个,就把老的模块上的功能关闭一个,逐 渐把用户引导到member1.taobao.com,等所有的功能都替换完之 后,关闭member.taobao.com上。
再说说用到的Java MVC框架,有个架 构师周悦虹,他在Jakarta Turbine的基础上做了很多扩展,打造了 一个阿里巴巴自己用的MVC框架WebX ,这个框架易于扩展,方便组件化开发,它的页面模板支持JSP和Velocity等,持久层支持 ibatis和hibernate等,控制层可以用EJB和Spring。
另外,当时Sun在全世界大 推广他们的EJB。在经历了很多次的技术讨论、争论甚至争吵之 后,这个系统的架构就变成了下图的形式。
另外,为了缓解数据库的压力,商品查询和店铺查询放在搜索引擎中。其实这个搜索引擎的原理很简单, 就是把数据库里的数据dump(倾倒)成结构化的文本文件后,放 在硬盘上,提供Web应用以约定的参数和语法来查询这些数据。 这看起来不难,难的是数以亿计的信息,怎么做到快速更新呢? 另一个难点是如何保证非常高的容量和并发量?
随着用户的增多,最采用了IOE这套方案。
Mysql数据库性能不够,换为Oracle,Oracle的存储一开始在本机上,后来在NAS 上,NAS撑不住了用EMC的SAN存储。
再后来,Oracle的RAC撑 不住了,数据的存储方面就不得不考虑使用小型机,让Oracle就运行在 了IBM的小型机。
存储方面,从EMC低端CX存储到Sun oem hds高端 存储,再到EMC dmx高端存储。
我们知道,一台Oracle的处理能力是有上限的,它的连接池有 数量限制,查询速度与容量成反比。简单地说,在数据量上亿、 查询量上亿的时候,就到它的极限了。要突破这种极限,最简单 的方式就是多用几个Oracle数据库,通过分库分表突破磁盘空间、内存、TPS、单机和单库的性能瓶颈和压力,IO、连接数、硬件资源等的瓶颈。
问题:1、联合查询困难 2、需要支持事务 3、跨库join困难 4、结果合并麻烦。
行癫出手了,他写了一个数据库路由的框架DBRoute,统一处理了数据的合并、排序、分页等操作,让程序员像使用一个数据库一样操作多个数据库里的 数据,这个框架在淘宝的Oracle时代一直在使用。
访问这个页面的时候,数据全都是只读的(全部从数据库中 读出来,不写入数据库),在那个时候,我们的架构师多隆做了一个基于 Berkeley DB 的缓存系统,把很多不太变动的只读 信息放了进去。其实最初这个缓存系统还比较弱,我们并不敢把 所有能缓存的信息都往里面放,一开始先把卖家的信息放里面, 然后把商品属性放里面,再把店铺信息放里面,但是像商品详情 这类字段太大的放进去受不了。
说到商品详情,这个字段比较恐 怖,有人统计过,淘宝商品详情打印出来平均有5米长,在系统里 其实放在哪里都不招人待见。笔者清楚地记得,我来淘宝之后担 任项目经理做的第一个项目就是把商品详情从商品表中移出来。 它最早与商品的价格、运费等信息放在一个表中,拖慢了整张表 的查询速度,而很多时候查询商品信息是不需要查看详情的。于 是在2005年的时候,我把商品详情放在数据库的另外一张表中, 再往后,这个大字段被从数据库中请了出来,先是放入了缓存系统,到现在是放进了文件系统TFS中。
CDN这个工作相对比较独立,跟别的系统一样,一开始我们 采用的也是商用系统。后来随着流量的增加,商用的系统已经撑 不住了,LVS的创始人章文嵩博士带人搭建了淘宝自己的CDN网 络。在本文的引言中,我说过淘宝的CDN系统支撑了800Gbps以上的流量。
通过对商用系统的优化,架构容量、 性能、成本都有了一定的改善。
另外,淘宝网还有很多文件需要存储,最主要的就是图片、商品描述、交易快照,一个商品要包含几张图片和一长串的描述信息,而每一张 图片都要生成几张规格不同的缩略图。在2010年,淘宝网的后端 系统上保存着286亿个图片文件。而且这些图片平均大 小为17.45KB。大规模的小文件存储与读取,因为磁头需要频繁寻道和换道,在读取上容易带来较长的延时。在大量高并发访问量的情 况下,系统将会有很大压力,商业系统已慢慢不能满足需要。
从2006年开始,我们决定自己开发一套针对海量小文件存储的文件系统,用于解决自身图片存储的难题。这标志着淘宝网从使用 技术到了创造技术的阶段。淘宝TFS文件系统在核心设计上最大的取巧在于:传统的集群 系统中元数据只有1份,通常由管理节点来管理,因而很容易成为 瓶颈。而对于淘宝网的用户来说,图片文件究竟用什么名字来保 存他们并不关心,因此,TFS在设计规划上考虑在图片的保存文 件名上暗藏了 一些元数据信息,例如,图片的大小、时间、访问 频次等信息,包括所在的逻辑块号。因此元数据结构非常简单。仅仅只需要一个FileID 就能够准确定位文件在什么地方。由于大量的文件信息都隐藏在 文件名中,整个系统完全抛弃了传统的目录树结构,因为目录树 开销最大。拿掉后,整个集群的高可扩展性可极大地提高。
图片命名例如:O1CN01WntMyi1Kso0jj4oth_!!0-item_pic.jpg_300x300q90.jpg
整个图片存储系统就像一个庞大的服务器,有缓存单元、处理单元和存储单元。
最上层则是一级缓存和二级缓存,前面还有 全局负载均衡的设置,用于解决图片的访问热点问题。目前淘宝网在各个运营商的中心点设有二级缓存,整体系统中心店设有一级缓存,加上全局负载均衡,传递到后端TFS的流量就已经非常均衡和分散了。
图片文件服务器集群用于生成缩略图的运算。如果缓存中无法命中,则会在本地服务器上查找是否存有原图,并根据原图生成缩略图,如果都没有命中,则会考虑去后台TFS集群文件存储系统 上调取
至此,最终反馈到TFS集群文件存储系统上的流量已经被大大优化了。
统计产品浏览量TBstore、用户中心TDBM合并为Tair(TaoBao Pair的意思,Pair即Key-Value数据对)Tair作为一个分布式系统,由一个中 心控制节点和一系列的服务节点组成,我们称中心控制节点为 Config Server,服务节点是Data Server。Config Server 负责管理所 有的Data Server,维护Data Server的状态信息。Data Server 对外 提供各种数据服务,并以心跳的形式将自身的状况汇报给Config Server。Config Server是控制点,而且是单点,目前采用一主一备 的形式来保证其可靠性。所有的Data Server 地位都是等价的。Tair 的架构图如下图所示。
到2008年初,整个主站系统的容量已经到了瓶颈。只有把底层的基础服务继续拆分,从底层开始扩容,上层才能扩展,这才能容纳以后三五年的增长。到2008 年年底就做了一个更大的项目,把淘宝所有的业务都模块化,这 是继2004年从LAMP架构到Java架构之后的第二次脱胎换骨。
基础业务:UIC 用户信息、Forest 类目属性服务。
核心业务:TC 交易中心、IC 商品中心、SC店铺中心 ,这些中心级别的服务只提供原子级的业务逻辑,如根据ID查找商品、创建交易。
业务系统: TM交易业务、IM商品业务、SM店铺业务、Detail商品详情。
另外,拆分之后的系统如何通信?这里需要两种中间件系统,一种是实时调用的中间件,一种是异步消 息通知的中间件。另外,一个需要解决的问题 是用户在A系统登录后,到B系统的时候,用户的登录信息怎么保 存?这又涉及一个Session框架。
那么在HSF出现之前,系统之间的调用采用什么方式呢? 这个有点“五花八门”,例如,对于类目的调用方式是: Forest打包成一个JAR包,在应用启动的时候装载到内存中,仅这一个JAR包所占用的内存就有800MB之多(因为淘宝的类目数据太庞大了),对于当时一般只有2GB内存的开发机来说,加载完类目信息后,机器运行速度就非常慢。对于用户信息(UIC)。来说,一开始的调用方式是用Hessian接口。还有一些系统是通过 WebService、Socket甚至是HTTP请求来相互调用的。每种调用方 式都涉及各种超时、信息的加解/密、参数的定义等问题,由此可见,在没有HSF之前,系统之间的调用是错综复杂的。而随着系统拆分得越来越多,必须由一个统一的中间层来处理这种问题, HSF正是在这种背景下诞生的。
服务拆分之 后,如何取得我需要的服务?
我们注意到ConfigServer并不会把服务提供者的IP地 址推送给服务的调用者,HSF框架会根据负载状况来选择具体的服务器,返回结果给调用者,这不仅统一了服务调用的方式,也实现了“软负载均衡”。平时ConfigServer通过和服务提供者的心 跳来感应服务提供者的存活状态。
HSF解决了服务调用的问题,我们再提出一个很早就说过的问题:
用户在银行的网关付钱后,银行需要通知到支付宝,但银行的系统不一定能发出通知;如果通知发出了,不一定能通知到;如果通知到了,不一定不重复通知一遍。这个状况在支付宝持续了很长时间,非常痛苦。然后鲁肃提出做一个 系统框架上的解决方案,把要发出的通知存放到数据库中,如果实时发送失败,再用一个时间程序来周期性地发送这些通知,系 统记录下消息的中间状态和时间戳,这样保证消息一定能发出, 也一定能通知到,且通知带有时间顺序,这些通知甚至可以实现事务性的操作。
他们之间也需要类似的通知。例如,拍下一 件商品,在交易管理系统中完成时,它需要通知商品管理系统减少库存,通知旺旺服务系统发送旺旺提醒,通知物流系统上门取货……用 户的一次请求,在底层系统可能产生10次的消息通知。这一大堆的通知信息是异步调用的(如果同步,系统耦合在一起就达不到拆分的目的)。
这些消息通知需要一个强大的系统提供支持,从消息的数量级上看,比支付宝和淘宝之间的消息量又上了一个层次,于是按照类似的思路,一个更加强大的消息中间件系统就诞生了,它的名字叫做Notify。Notify是一个分布式的消息中间件系 统,支持消息的订阅、发送和消费。
工作流程
NotifyServer可以水平扩展,NotifyClient也可以水平扩展,数据库也可以水平扩展,从理论上讲,这个消息系统的吞吐量是没有上限的,现在Notify系统每天承载了淘宝数亿次以上的消息通知。
还有一个制约系统规模的更重要的因素,就是数据库,也必须拆分。淘宝很早就对数据进行过分库的处理, 上层系统连接多个数据库,中间有一个叫做DBRoute的路由来对 数据进行统一访问。DBRoute对数据进行多库的操作、数据的整合,让上层系统像操作一个数据库一样操作多个库。但是随着数据量的增长,对于库表的分法有了更高的要求,例如,你的商品数据到了百亿级别的时候,任何一个库都无法存放了,于是分成 2个、4个、8个、16个、32个……直到1024个、2048个。
好,分成 这么多,数据能够存放了,那怎么查询它?这时候,数据查询的中间件就要能够承担这个重任了,它对上层来说,必须像查询一个数据库一样来查询数据,还要像查询一个数据库一样快(每条 查询在几毫秒内完成),TDDL就承担了这样一个工作。另外,加上数据的备份、复制、主备切换等功能,这一套系 统都在TDDL中完成。
TDDL(—Taobao Distributed Data layer)实现了下面三个主要的特性:
因为HTTP协议本身是无状态的,所以经常需要通过Session来解决服务端和浏览器的保持状态的解决方案。
Session中存储的内容包括用户信息:昵称、用户ID、登录状 态等。 当网站服务器只有一台的时候,用Session来解决用户识别是很简单的,但是当网站是一个集群的时候,同一用户的两次请求可能被分配到两台不同的服务器上处理。怎样保证两次请求中存 取的Session值一致呢?还有一个问题:网站规模扩大时,对于一 个具有上亿个访问用户的系统来说,当大部分用户的Session信息 都存储在服务端时,要在服务端检索出用户的信息效率就非常低 了,Session管理器不管用什么数据结构和算法都要耗费大量内存 和CPU时间。如何解决服务端Session信息的管理?这种情况下,Tbsession框架闪亮登场了。
tbsession是如何解决这些问题的?
⦁ 结合客户端和服务端来存储用户session信息;
⦁ 客户端采用cookie存储;
⦁ 服务端使用tair存储;
⦁ 通过配置文件决定存储在cookie中 或 tair中(我们现在应用一般都采用tair存储);
⦁ 配置文件存储在diamond-server上,实现动态更新;
当一个请求到达服务端的时候,服务器会判断这个请求是否带有sessionID标识,如果没有,服务端会为该请求创建一个sessionID,如果该请求带有sessionID,则服务端会根据这个sessionID把该session取出来使用。
到此为止,应用服务、核心服务、基础服务、数据存储都做了切分,通过高性能服务框架(HSF)、分布式数 据层(TDDL)、消息中间件(Notify)和Session框架支持了这些 切分。一个美高度稳定、可扩展、低成本、快速迭代、的淘宝的3.0系统走上了历史的舞台。在这个分布式系统的支持下,更多的业务迅速开发出来了, 因为任何一个业务都基于淘宝的商品、交易、会员、评价等基础体系,而这些基础体系就像“云”一样存在,现在可以随处调用了。Hitao、淘花网、良无限、天猫、一淘、聚划算、各种SNS、 各种移动客户端等如雨后春笋般地成长起来了。
本文大致梳理了03年成立到14年的架构演进,最近10年的变化等下次有时间了再整理。
《淘宝技术这十年》