我设计的12306

feed系统和火车票售卖系统是2个高访问高并发情况下具体很大挑战的系统。
在低访问,低并发的情况下feed系统会变的非常简单,数据模型和业务功能都比较容易设计和实现,主要的挑战就剩如何面对层出不穷的敏感词和花样百出的广告语。相比之下,火车票售卖系统在低并发时也很有趣,假设我是12306的架构师,我会如何设计12306那。

数据模型

先将系统进行拆分,独立成用户,车票,下单3个系统,每个系统内部封闭成多个服务,运行在独立的集群上面。这里只对车票系统进行数据模型设计。

先上ER图 数据模型

数据分成动静2部分,车次,站点,座位的数据都是静态的基本不会变,可以通过运营系统提前进行录入生成;车票则根据以上三张表每天动态生成,每条车票记录一个车次上的一个座位号,初始化的始发站,终点站为该车次的始发站和终点站,数据在下单时进行更新。确定了数据模型后进行数据库的垂直和水平拆分,首先按照车次进行分库,将不同的车次hash到几个数据库中,减少每个数据库的负载;然后车票表按天拆分,提前生成3个月的车票表,每张车票表只存储当天发车的车票。

用例

  • case1:查询
    假设有一列从北京到深圳的火车D911,途经共20站。用户查询北京到杭州的列车,从cache中取出符合用户查询条件的车次(车次类型,始发站,终点站,始发时间,到站时间),按照车次从cache中取出北京和杭州的站点id和站序,北京站序为0,杭州站序为11。车票表中用
    ‘始发站序<=0 and 终点站序>=11 and 车次=D911 group by 座位类型’
    的查询条件即可得到每种座位的剩余票数,可以将查询的结果做一个10s的缓存,如果前端展示的不是具体的剩余票量,而是有无票,可以使用一个更长时间的缓存,缓存的失效由服务端控制。
  • case2:车票分拆
    假设有一列从北京到深圳的火车D911,用户购买了一张从北京到杭州的车票,按照规则优先级随机取出一张车票,车票数据为 北京-深圳,始发站序0,终点站序20,将该车票状态置为无效,插入一张数据为杭州-深圳,始发站序11,终点站序20的车票,向下单系统发起请求,写入一张北京-杭州的订单。
  • case3:车票合并
    假设有一列从北京到深圳的火车D911,用户预订了北京-杭州的车票,从下单系统收到该订单失败或者超时或者退票的消息,取出其中和车票相关的信息,车次D911,北京-杭州,始发站序0,终点站序11,和车票表中该车次该座位的数据进行关联,合并数据,重新生成一条北京-深圳的车票。

队列·流控·异步·无锁实现

系统的抗压能力和是吞吐量成正比的,这也就是为什么静态页面可以支持超高的QPS,查询的性能优化也比较容易,事务处理的性能提升最困难,系统处理时会保持tcp链接,占用系统资源,最终导致系统的崩溃,响应时间越快,资源的占用时间越短,吞吐能力也就越强,系统的可用性也就越高。
在某宝某猫做话费充值系统的思路完全可以用来做火车售票系统,将下单请求持久化,系统间通过消息解耦,通过多线程队列异步处理请求。具体的实现可以在收到下单购票请求后持久化,返回给用户一个排队中的提示(1-x分钟处理完成),按照车次放到不同的队列中进行排队(期间可以做过滤/去重/合并处理),系统从队列中取数据进行处理。最终一致性就可以满足业务需求的地方,服务尽量减少事务和锁的使用,提高并发处理能力。

降级

为啥要把降级单独拉出来说,我觉得本质上讲降级并不是由于架构设计上的充分考虑带来的可用性和伸缩性的提高,而是牺牲一部分的用户体验换来的系统的可用性,是对峰值事件的应对策略。基本实现就是在系统埋好各种开关,可以由人工控制也可以由系统触发,保证最基本的核心功能可用,其他的非核心功能和部分用户体验可以暂时舍弃。

数据模型和业务功能就是这样,不论实现的多扭曲基本上大家都可以做出来,功能实现之后无bug是一个挑战,能够满足未来变化是一个挑战,在某个量级之后依然可用又是一个挑战。

posted @ 2017-02-23 11:08  minotaursu  阅读(676)  评论(0编辑  收藏  举报