如果让我设计12306

本文仅仅是12306系统设计的一个讨论区,并不是权威的系统设计论文,本人在阅读一些人的争论和讨论后,对需求和其他人的看法做了个总结,并且想从中选择一个最佳方案,希望大家多出意见。

更多讨论来自于:http://www.it168.com/redian/12306hpc/

设计的关键点:

1. 14亿(PV)每天的流量集中在几分钟之内,每秒上千万流量(QPS),考虑到C10K,需要上千台机器,读写操作短而高并发,需要缓存数据,牺牲一致性。
2. 可用SSD的告诉读取特性充当缓存层,仅当数据库变化时更新缓存,这样极大的解决了数据库的频繁查询带来的压力。
3. 12306是在票数小于一定数值的时候才通知查询用户,否则只是显示有票还是没票,例如在tickets < 10, 从数据库更新最新数据到缓存。
4. Facebook的特点是瞬间并发写操作非常频繁,facebook是用hbase解决瞬间写的问题,hbase在读取的时候要合并LSM树,所以,hbase的读性能并不好。
5. 12306是瞬间的读写操作全都高并发。
6. QQ在线用户多,也是高并发,但是各自访问自己的数据,或者某些qq之间通讯,不像订票系统是需要访问中心数据库的。
7. 这与淘宝的秒杀活动也不一样,秒杀都是做到只接受前N个用户进来操作,其他用户都被阻挡在前端服务器了,或者在浏览器的js中就被秒杀了。
8. 12306在尖峰时刻,系统压力是平时的10倍,可达到每秒1000 0000级别的QPS,服务器没法响应,请求就堆积在web server或者请求队列里。
9. 票务相关的查询都是直线型的,不一定非要到关系数据库里面去查,所有用key-value数据库或者缓存正合适。
10. 10亿 IOPS/day, 1000 0000/s QPS
11. 读写分离,拆分, 分库分表,分片。
12. 12306在高峰期流量是平时的n倍,所以考虑动态基础架构,例如 IAAS。
13. key(站A,站B,乘车日期) Value(车次的集合)
key(站A,站B,乘车日期,车次) value(余票数量)
缓存是永久的,除非被清除或者更新。
14. 2064对列车,每列车有20个站,1000个位置。
15. 单个memcached处理性能上是极高的,每秒达到上千万次是轻松自如的。
16. 如果一个人买了从起点到终点的票,那么影响所有的车站,但是如果一个人买了中间一段的票,那么起站前和终站后的票不影响。
17. 前端的设计要在满足功能需求的前提之下做得比较轻,所谓轻就包括前端的格式和数据量,每下载一个页面所需要的数据量以及打开同一个页面上建立的http的连接数要尽可能地少。反之,如果网页比较重的话,每下载一个网页从服务器传出来的东西就会比较多,当访问量很大时,对系统的带宽会有极大的挑战。
18. 12306前端设计比较重,主要表现几个方面:第一,把CSS的表和网页混在一块下载。第二,打开一个网页需要建立的http连接数都很多。很多关键的网页包括订票的网页、查询的网页,打开一个网页需要建立的http连接数都很多,有人分析过打开一个网页超过70个http连接数。每一台服务器建立的http连接数是有一个上限,如果大家都去并发访问的时候,服务器的连接数很快就不够用了。
19. 如果涉及到关联订票的情况就更复杂了,但是目前12306还没有实现关联订票。
20. 有很多电商的网站实际已经从业务逻辑的调整为一个异步,就是把订票这个事务变成异步操作来完成。
21. 同时最好都在一个http连接或者很少在http连接就能把这个页面下载过来或者上传回去。
22. 第一,查询的和交易等功能尽可能地划分,由不同的服务器来做相关的事情。第二,从数据上划分,如果是一个数据库,尽可能地要把一张很复杂的大表分给各种小表,尤其不相关的信息,尽可能拆分到不同的表里甚至不同的数据库里。举一个例子,像淘宝很简单的一个划分方法就是以网店为单位设计这个表,一个极端的例子就是每个网店是一张表,当然这个例子很极端,它肯定不是这么做的,一个网店一张表的话,表的数量肯定太多了。我查了一些资料,在春运的时间根据铁道部发布的全国列车运行图,在2012年春运期间好象开了2064对列车,来往算一对的话,总共开了2064对。如果把一对看成一个网店话,就是开了2064个网店,每一个网店里面每一条铁路线,实际就是任何每两点都应该可以买到票,理论上讲可以买到票,如果一条线上N个节点,应该是(28:09ncr)都可以买到票。这么一个组合,这个数字出来相当于网店里的商品数。这个量也不算太大,如果以每一对列车作为一个处理单元,可以把(28:37)分开的。
23. 还有一招就是尽可能地把同步操作变成异步操作。所谓异步操作就是它不需要马上给你回复。举一个例子,大家都用过携程网,携程网订飞机票,大家知道在携程里选了以后就下单了,下单以后携程不会马上告诉你订票成功的,它会告诉你它会去处理,会把处理的结果给你,通过短信通知你。
24. 但是,据我知道,没有人用NOSQL的技术做交易系统。

架构图

IMG_20140723_204107

Tips

1. Ticket按照车次分片足以,Order按照用户分片。
2. 比如说,1万个人看见还有票(其实还有15张),其中9975个人都点击了订票,那么9975个请求全部进入了第一个Q,让请求超时是最后一个屏障,超时前,但是票已卖光后,就批量处理未订上票的请求,并且缓存未定上票信息在cache中,客户要主动查询,而不是消息推送到web app,用户订票请求了但是最后没定上的信息,缓存在cache里,不需要持久化IO,不需要保存数据库,这部分信息在用户看完后就没用了,用户不会永远关心的,缓存1天或者2天,时间过后就清除,过了1天或者2天,客户不再关心没有定上票的请求了,可以设置缓存超时,由于不缓存数据库,这部分批量处理是很快的。

FAQ

1. Cache里面如何存这些车票数据? 数据库里面的座位号怎么存?单独一个表还是存在ticket表里。
2. 没票,过期,批量处理,存cache,应该再单独的说明和设计。

发表评论