难得在博客园上能看到案例设计讨论,而且是关于12306售票系统的。于是我也来发表下的我一些感想,欢迎来吐槽!
12306系统升级了,但还是被吐槽了。这次国庆我没买火车票,按照去年春节的购票经历,我谈谈我的看法。
什么才是12306最需解决的问题?
1、 重大节假日前期,系统登陆难。
2、 抢票环节的并发处理能力。
3、 余票查询的响应速度。
人们往往有先入为主的观念,导致了解决问题的思维方式收到束缚,难以跳出既定的圈子。谁能说现在的购票系统的业务逻辑和用户体验是最合理的呢?它的设计合理之处又在哪里呢?我想完全不懂技术的人做产品经理,可以比技术出身的人做的更好,因为不懂技术的产品经理提出的需求不会受技术的束缚,更加注重用户体验。12306的余票查询的用户体验太糟糕了,为什么非要有帐号才可以查询-_-
购票系统的功能架构和技术架构,势必要考虑到峰值的处理能力,尤其是超大规模并发的处理能力。12306虽说是非盈利性的,但是这毕竟关乎到民生,为何不公开技术架构,让更多的人参与改进呢?
以上内容可以忽略。以下是我的设计思路,主要采用功能适度分离的思想进行设计。
1、 余票查询的优化方案
将余票查询系统与抢票系统功能分离,余票查询系统部署到镜像站点CDN上,抢票系统应用单独部署,支付和退票应用单独部署。(这点很关键)
数据库的读写分离,主数据库只做写操作,写入购票记录和更新实时余票信息,余票查询库可以通过异步更新获取余票信息。余票查询功能可以基于部署到CDN上,建议免登陆,建议向社会开放余票资源和API。(解决查票问题)
余票查询系统的系统架构。我们需要什么级别的实时性?毫秒级?不需要!余票查询的操作远大于抢票,每1秒内信息的变化都难以想象,所以在余票查询上实时性太高意义不大,只能作为抢票前的参考,所以也没必要一定要用关系数据库,NOSQL其实很合适。系统只要保证购票者在信息获取是平等的,抢票环节是公平的(按照先购先得的原则),然后进行适度优化设计。余票查询的系统架构,我有2个设计方案
1) 内存数据为主、数据库为辅方案。
写一个分布式数据分发系统(主站为SApp,镜像站点为CApp),支持远程调用更新,设计特定的类或数据结构,将所有待出售的车票余票信息存储在特定的类或数据结构中(也可以是数据缓存),常驻内存。余票信息变更后,更新SApp。SApp与CApp的数据同步可以设置为15秒1次(或1min一次甚至更久,没数据变化当然不需要去更新啦,因为查询这块应用没必要做到秒级的同步)。SApp的故障恢复,数据可以通过DB(DB可以是一个数据库群集或NOSQL)获取;而CApp的故障恢复,就直接从SApp进行提取即可。如果余票信息不存在于本CApp中,则直接通过算法,从对应的CApp_x获取,如果对应的CApp_x中数据不存在,则直接从SApp获取,并告诉SApp应该去更新CApp_x数据。
2) 纯数据库方案。
基于车票余额信息进行分库,并进行数据库远程同步。考虑到实际应用特点,可以对余票信息按照部署城市或者地区进行分库。如在杭州买票,基本上都是查询涉及包含杭州的车次信息,所以可以根据这点做深入的优化,增加冗余记录,减少跨库查询。所以这些数据的实时性要求会比较高,要直接从CDN的镜像库获取。出现异地代购性质的余票查询时,通过主数据库的读库(或者对应CDN的镜像库)进行查询。
2、 抢票环节的优化方案
我可以理解在火车站买票需要排队,因为这样可以维持秩序,对先排队的人也是一种。但是无法理解在网络上买票还需要排队,难道排到后一定有票?更无法理解登陆还要排队,因为把购票者堵在外面不是解决问题的办法,提高窗口的并发处理能力才是关键。
消息队列是需要的,但不应该出现在登陆和购票环节。虽说抢票靠的是硬件、软件、网络和人品,但是每个人都应该同享购票的权利。如何做到这点?
1) 抢票处理服务和身份认证必须分开部署,前端通过负载均衡提高并发处理能力。
2) 将每个抢票请求提交到对应的消息队列(不同车次的抢票处理服务,可以部署在不同的服务器上),并返回队列位置ID,抢票环节完成。然后就是等待抢票决策系统的处理结果。保证每个请求都会被登记并被处理,这点才能体现抢票环节的公平、公正、公开。
3) 异步处理抢票消息队列中的每个信息。抢票成功(表示抢票信息被处理后,受理通过),则给予车票支付序列号,按照序列号支付,完成购票环节。
4) 用户抢票申请的被接收和被处理过程分开。抢票被接收后,如果当前车次的未处理消息队列大于设定的阀值,则堵塞新的抢票申请。如果消息队列数小于阀值,已经没有余票,但是还有未完成支付的余票,则询问用户知否排队等待(如果有人退票或未能完成支付,则享受优先获得该票的资格,在退票和支付过期处理系统中优先考虑这部分申请)。
5) 单独部署未支付订单审查系统。每分钟检测超时未支付订单,并将失效的购票申请反馈到相应的队列处理服务器。
6) 用户可以在有效时间内完成支付工作。可以通过在线支付平台,完成对特定车票支付序列号的支付。支付应用最好也单独部署,加入负载均衡。