浅谈12306的架构设计

Page content

既然有人问12306这种网站如何设计,我不才,来简单说几句。 忽悠之前先来了解一些基本现状。

  1. 按照铁道部公开的数据,注册用户大约在5000万,日访问PV大约在10亿,每日网上订购票大约在500万
  2. 每一个个人用户的数据都是独立的,不会和别人共享
  3. 每一个铁路局(全国18个铁路局)下管理很多小的车站,每一趟车票的数量控制基本上有其所属的铁路局分配
  4. 每一个用户的数据非常有限,由于是新上线的网站,平均应该在2张以下(到目前为止应该还没有卖出1亿张票),我们以最大估计没人平均在10张左右
  5. 比较耗时的操作应该集中在支付对接(不排除要求银行提供更有效的接口)
  6. 用户查询的需求远远大于订票的需求
  7. 定时发票可能催生秒杀,访问量瞬时上升

基于以上事实,以下给出几点主要的建议:

1. 业务拆分,资源拆分

对于和订票无关的业务以及资源,例如帮助、政策、新闻、静态资源等等拆分成不同的子域名,减低主域名的干扰以及压力。 业务的拆分可以分成注册、查询类(一些固定资源的查询也可以CDN的,比如车次、时间、车站等等)、交易类、支付类、其它静态类等业务。 现状是一旦网站访问慢时,访问任何资源都非常慢。 拆分成子域名后还可以有效提高浏览器加载css/js的并发量数量。

2. 提高CDN的效率

业务拆分,资源拆分后,与个人无关的所有访问资源都应该纳入CDN,充分利用CDN加速的效果。尤其是js以及CSS这一块,CDN需要承担上99.99%以上的访问量。降低后台服务器的压力。 事实上对于个人而言,除了查询、订票以及支付外(注册的访问量是一次性的,独立拆分就好了),大部分内容都是可以走CDN的。 现状是CDN商为了提高流量,基本上没有发挥出CDN的效果。

##3. 数据库拆分

按照铁路局或者省份拆分数据库,访问量高的铁路局或者省份可以多拆分几个数据库。这可以极大降低数据库的压力。一个简单的原则是这些数据基本上是不共享的,而且只有个人查询的需求。不存在跨省、批量查询等需求。 如果按照oracle或者db2的性能,按照写数据那点量是一点问题都没有的。而且对于个人来说一旦一张票支付成功,基本上也不会查看,因此可以定时归档,数据库的数据量也是非常小的。 对于个人而言,按照身份证号码的所属或者尾号等等拆分即可,也降低个人数据的数据库压力。

4. 数据缓存

目前12306的需求是数据缓存10分钟(主要指余票的数量),因此应该大量增加Cache的利用率。例如memcache/redis甚至业务拆分好的话,本地cache也不是不可以的。 而且,如果充分利用nosql的特性的话基本上可以做到数据实时性,不需要缓存10分钟降低用户的体验(提交订单最后一刻总是失败查询却总是有票是令人很崩溃的)。 主要的数据缓存应该集中在车次以及余票上,其它的数据缓存问题都不大。 即使穿透了缓存,最终也要靠数据库的事务保证逻辑的正确性。

5. 用户体验

这年头还是用iframe的体验简直烂到家了。 虽然极少量使用了ajax,但是基本上也没有发挥出威力。即使一次ajax请求也请求了大量无关的资源,造成大量的网络带宽浪费。 为了提高效率,最重要一点就是大量提高ajax的访问量。不仅降低服务器的压力,也要减少传输的耗时。这一块怎么也得专业一点吧。 一个用户登录进去以后的动作其实非常简单,只需要查询、下订单、支付几个步骤,而且绝大多数集中在查询操作上,因此如何降低用户查询的成本、提高查询的用户体验远远比换一个好看的皮肤重要的多。 数据传输这一块应该大量使用json而不是笨重的xml。js对json的支持远远比xml要容易得多,而且json数据量更小更轻便,后端生成json的成本也更低。毫无疑问,使用xml不是一个明智的选择,尤其对于高访问量、大数据库传输。

6. 流量控制

流量控制非常重要。有效的控制流量可以降低并发的数据量,降低服务器临时崩溃的可能性。将压力平摊到各个时间段远远比在短时期内消耗掉要安全得多。 验证码是一个比较好的措施,只是目前有两个缺陷。很多地方的验证码只是在前端做了限制,如果不传输验证码后端也允许通过(难道这是他们为了内部方便测试留的)。这导致大量的工具绕过验证码登录了。第二点,早期的验证码非常简单,导致大量的工具能够自动识别验证码。尽管这些工具的识别率不高,但是由于工具重复操作,造成了大量无用的性能浪费以及流量浪费。真正有需求的人反而没有机会操作。 首先,后端增加验证码的逻辑。无效的访问请求很快就过滤掉了。 其次,验证码不要和session绑定,直接使用单向加密算法传输即可,简单快速。 再次,验证码做得智能点的话可以根据访问量逐渐提高复杂度,甚至不惜动用中文未尝不可。增大ocr识别的成本。 最后,极端情况下控制访问人数是有必要的,但是应该保证能够查询这种基本操作。

7. 支付与安全

其实对于这种业务比较单一的系统,实现起来还是比较容易的。稍微麻烦点就是与银行的对接以及事务的控制。 12306最后和银行的对接还比较少,主要是和银联合作。况且,以这种优势地位,需要什么样的接口还不是人家乖乖提供的。 一旦订单提交成功后就锁定票,等待用户支付。支付操作的压力基本上定向到银行以及银联的支付网关了。另外按照每天500万票来算,每秒钟的压力是非常小的。 有点麻烦的可能就是支付失败或者中间某个环节失败后事务如何回滚的问题。毕竟这需要保证用户的资金安全的。 其实有统一的支付网关接口,操作起来是非常简单的。大部分提交应该都是能够在一个事务内完成的。极端情况下如果有部分操作失败,应该立即将这些操作提交到后台,进行人工审核和处理。而且对于人工来说也无非几个操作,是否扣款(查询支付网关)、是否出票(查询出票记录),然后对比下是否需要退款或者退票等。

8. 流程控制

最后规划化上线流程非常必要。不至于在线调试代码的情况发生。 基本的操作规范都没有,如何保证代码的质量以及系统的可靠性。 网上的戏言是这只是某个学院学生的作业。可见网站的整理水平确实过于低下,没有规范的流程控制,怎么方便怎么来,没有风险意识,更没有问责机制。 按照一般体制内的要求肯定是过了测试流程的,同样肯定也是走走过场的。显然没有进行过多的压力测试和性能测试。代码的质量也不高,基本的业内规范都没有遵守。

再好的想法和规划,没有规范的流程最后还是会打水漂的。 最后合理的规划业务逻辑是必要的,仅仅靠技术和架构是不能解决所有问题的。充分利用CDN加速、负载均衡、业务拆分、nosql加速、有效的安全机制等措施,保证系统在瞬时能够抗住巨大的压力,同时能够平稳处理用户需求。

对于这种简单业务逻辑的系统是非常适合进行扩展和优化的。

一句话:铁道部给程序员的钱不够!!!