2011年11月11日,这个棍子最多的日子被网民自我调侃地变成了一个节日——“光棍节”,而淘宝网又用疯狂的折扣促销给它赋予了另外一个意义——购物狂欢节,11月11日这一天,淘宝商城与淘宝网交易额之和突破52亿元人民币,这个数字是购物天堂香港一天零售总额8.5亿元的6倍。
10年前的的淘宝页面
淘宝能够举办如此盛宴,网站的技术实力可见一斑。至2011年年底,淘宝网拥有全国最大的Hadoop分布式计算集群之一(2000多个节点,CPU:24000 core,Memory:48000GB,Disk:24000块),日新增数据50TB,有40PB海量数据存储,分布在全国各地80多个节点的CDN网络,支撑的流量超过800Gbps。淘宝的搜索引擎能够对数十亿的商品数据进行实时搜索,另外,还拥有自主研发的文件存储系统和缓存系统,以及Java中间件和消息中间件系统,这一切组成了一个庞大的电子商务操作系统。
Java时代
2004年初,Java是当时最成熟的网站开发语言,它有比较良好的企业开发框架,被世界上主流的大规模网站普遍采用。另外,有Java开发经验的人才也比较多,后续维护成本会比较低。
到2004年上半年,淘宝网已经运行了一年的时间,这一年积累了大量的用户,也快速开发了很多功能,当时这个网站已经很庞大了,而且新的需求还在源源不断地增加,对于开发模式,用到的Java MVC框架,当时的struts1.x是用得比较多的框架,但是用过webwork和struts2的人可能知道,struts1.x在多人协作方面有很多致命的弱点,由于没有一个轻量框架作为基础,因此,很难扩展,这样架构师对于基础功能和全局功能的控制就很难做到。而阿里巴巴的18个创始人之中,有个架构师周悦虹,他在Jakarta Turbine的基础上做了很多扩展,打造了一个阿里巴巴自己用的MVC框架WebX ( www.openwebx.org/docs/Webx3_… ),这个框架易于扩展,方便组件化开发,它的页面模板支持JSP和Velocity等,持久层支持ibatis和hibernate等,控制层可以用EJB和Spring(Spring是后来才有的),项目组选择了这个强大的框架。
MVC框架是阿里的WebX,控制层用了EJB,持久层是ibatis。另外,为了缓解数据库的压力,商品查询和店铺查询放在搜索引擎中,截止到2004年底,淘宝网已经有4百多万种商品了,日均4千多万个PV,注册会员达400万个,全网成交额达10亿元。
随着数据量的继续增长,到了2005年,商品数有1663万个,PV有8931万个,注册会员有1390万个,这给数据存储带来的压力依然很大,数据量大,速度就慢。亲,除了搜索引擎、分库分表,还有什么办法能提升系统的性能?一定还有招数可以用,这就是缓存和CDN(内容分发网络)。
分布式时代
到2008年初,整个主站系统(有了机票、彩票系统之后,把原来的系统叫做主站)的容量已经到了瓶颈,商品数在1亿个以上,PV在2.5亿个以上,会员数超过了5000万个。这时Oracle的连接池数量都不够用了,数据库的容量到了极限,即使上层系统加机器也无法继续扩容,淘宝只有把底层的基础服务继续拆分,从底层开始扩容,上层才能扩展,这才能容纳以后三五年的增长。
于是淘宝开始把交易这个核心业务模块拆分出来。原来的淘宝交易除了跟商品管理耦合在一起,还在支付宝和淘宝之间转换,跟支付宝耦合在一起,这会导致系统很复杂,用户体验也很不好,淘宝开始把交易的底层业务拆分出来,叫交易中心(Trade Center,TC),所谓底层业务,就如创建订单、减库存、修改订单状态等原子型的操作;交易的上层业务叫交易管理(Trade Manager,TM),例如,拍下一件普通商品要对订单、库存、物流进行操作,拍下虚拟商品不需要对物流进行操作,这些在TM中完成。这个项目取了一个很没有创意的名字——“千岛湖”,开发人员取这个名字的目的是想在开发完毕之后,去千岛湖玩一圈,后来他们如愿以偿了。这个时候还有一个淘宝商城的项目在做,之前拆分出来的那些基础服务给商城的快速构建提供了良好的基础。
类目属性、用户中心、交易中心,随着这些模块的逐步拆分和服务化改造,在系统架构方面也积累了不少经验。到2008年年底就做了一个更大的项目,淘宝把所有的业务都模块化,这是继2004年从LAMP架构到Java架构之后的第二次脱胎换骨。
这时候淘宝系统的拆分架构:
其中,UIC和Forest在上文已说过,TC、IC、SC分别是交易中心(Trade Center)、商品中心(Item Center)、店铺中心(Shop Center),这些中心级别的服务只提供原子级的业务逻辑,如根据ID查找商品、创建交易、减少库存等操作。再往上一层是业务系统TM(Trade Manager,交易业务)、IM(ItemManager,商品业务)、SM(Shop ,Manager,后来改名叫SS,即Shop System,店铺业务)、Detail(商品详情)。拆分之后,系统之间的交互关系变得非常复杂,示意图如下所示。
系统这么拆分的好处显而易见,拆分之后的每个系统可以单独部署,业务简单,方便扩容;有大量可重用的模块便于开发新的业务;能够做到专人专事,让技术人员更加专注于某一个领域。这样要解决的问题也很明显,分拆之后,系统之间还是必须要打交道的,越往底层的系统,调用它的客户越多,这就要求底层的系统必须具有超大规模的容量和非常高的可用性。另外,拆分之后的系统如何通信?这里需要两种中间件系统,一种是实时调用的中间件(淘宝的HSF,高性能服务框架),一种是异步消息通知的中间件(淘宝的Notify)。另外,一个需要解决的问题是用户在A系统登录后,到B系统的时候,用户的登录信息怎么保存?这又涉及一个Session框架。
中间件
用户在银行的网关付钱后,银行需要通知到支付宝,但银行的系统不一定能发出通知;如果通知发出了,不一定能通知
到;如果通知到了,不一定不重复通知一遍。这个状况在支付宝持续了很长时间,非常痛苦。支付宝从淘宝剥离出来的时候,淘宝和支付宝之间的通信也面临同样的问题,那是2005年的事情,支付宝的架构师鲁肃提出用MQ(Message Queue)的方式来解决这个问题,当发现消息数量上来之后,常常造成拥堵,消息的顺序也会出错,在系统挂掉的时候,消息也会丢掉,这样非常不保险。于是淘宝提出做一个系统框架上的解决方案,把要发出的通知存放到数据库中,如果实时发送失败,再用一个时间程序来周期性地发送这些通知,系统记录下消息的中间状态和时间戳,这样保证消息一定能发出,也一定能通知到,且通知带有时间顺序,这些通知甚至可以实现事务性的操作。
数据库拆分
淘宝很早就对数据进行过分库的处理,上层系统连接多个数据库,中间有一个叫做DBRoute的路由来对数据进行统一访问。DBRoute对数据进行多库的操作、数据的整合,让上层系统像操作一个数据库一样操作多个库。但是随着数据量的增长,对于库表的分法有了更高的要求,例如,你的商品数据到了百亿级别的时候,任何一个库都无法存放了,于是分成2个、4个、8个、16个、32个……直到1024个、2048个。好,分成这么多,数据能够存放了,那怎么查询它?这时候,数据查询的中间件就要能够承担这个重任了,它对上层来说,必须像查询一个数据库一样来查询数据,还要像查询一个数据库一样快(每条查询在几毫秒内完成),TDDL就承担了这样一个工作。
TDDL实现了下面三个主要的特性:
- 数据访问路由——将针对数据的读写请求发送到最合适的地方;
- 数据的多向非对称复制——一次写入,多点读取;
- 数据存储的自由扩展——不再受限于单台机器的容量瓶颈与速度瓶颈,平滑迁移。
一个简单的分库分表数据查询策略: