对12306系统的服务以及售票系统有了进一步的了解:数据库
其实,12306系统也至关因而电商系统,并且看起来商品就是票了。由于若是把一张票当作是一个商品,那购票就相似于购买商品,而后每张票都有库存,商品也有库存的概念。可是若是咱们仔细想一想,会发现12306要复杂不少,由于咱们没法预先肯定好全部的票。缓存
对于12306系统来讲,他的用户无异因而是二者,其一就是咱们这些进行出行购票普通用户,另外一方就是要售票的铁道部。微信
对于传统的电商系统来讲,若是按照普通电商的思路,把票(站点区间)设计为商品(聚合根),而后为票设计库存数量。我我的以为是很糟糕的。由于一方面这种聚合根很是多,另外一方面,即使枚举出来了,一次购票也必定会影响很是多其余聚合根的库存数量(只要被部分或所有重叠的区间都受影响)。这样的一次订单处理的复杂度是难以评估的。并且这么多聚合根的更新要在一个事务里,这不是为难数据库吗?并且,这种设计必然带来大量的事务的并发冲突,极可能致使数据库死锁。总之,我认为这种是典型的因为领域模型的设计错误,致使并发冲突高、数据持久化落地困难。架构
对12306系统来讲,车次具备一次出票的全部信息,因此咱们应该把出票的职责交给车次。咱们应该知道,聚合设计有一个原则,就是:聚合内强一致性,聚合之间最终一致性。对于系统售票,要产生一张票,其实要影响不少和这个票对应的直线相交的其余票的可用数量。由于全部的站点信息都在车次聚合内部,因此车次聚合内部天然能够维护全部的原子区间,以及每一个原子区间的可用票数(至关因而库存数)。当一个原子区间的可用票数为0的时候,意味着火车针对这个区间的票已经卖完了。因此,咱们彻底可让车次这个聚合根来保证出票时对全部原子区间的可用票数的更新的强一致性。对于车次聚合根来讲,这很简单,由于只是几回简单的内存操做而已,耗时能够忽略。一列火车假若有ABCD四个站点,那原子区间就是3个。并发
根据订单信息,拿到出发地和目的地,而后获取这段区间里的全部的原子区间。而后尝试将每一个原子区间的可用票数减1,若是全部的原子区间都够减,则购票成功;不然购票失败,提示用户该票已经卖完了。是否是很简单呢?知道了出票的逻辑,那退票的逻辑也就很简单了,就是把这个票的全部原子区间的可用票数加1就OK了。若是咱们从线段的厚度的角度去考虑,那出票时,每一个原子区间的厚度就是+1,退票时就是减一。就是相反的操做,但本质是同样的。分布式
在用户进行购票付款的状况下,用户有可能不去付款或者没有在规定的时间内完成付款。那这种状况下,系统会自动释放该用户以前订购的票。因此基于这样的需求,咱们在业务上须要支持业务级别的2pc。即先预扣库存,也就是先占住这张票必定时间(好比15分钟),而后付款成功后再真实给你这张票,系统作真正的库存修改。性能
在系统进行购票查询的时候,对于12306来讲,查询的请求占了80%,提交订单的请求只占20%。但查询因为对数据没有修改,因此咱们彻底可使用分布式缓存来实现。咱们只须要精心设计好缓存的key便可;缓存key的多少要当作本,若是全部可能的查询都设计对应的key,那时间复杂度为1,查询性能天然高;但代价也大,由于key多了。若是想key少一点,那查询的复杂度天然要上去一点。因此缓存设计无非就是空间换时间的思路。而后,缓存的更新无非就是:自动失效、定时更新、主动通知3种。经过CQRS架构,因为CQ两端是事件驱动的,当C端有任何状态变化,都会产生对应的事件去通知Q端,因此咱们几乎能够作到Q端的准实时更新。设计
文章部分总结摘自原文,向原做者致敬!若有侵权或不周之处,敬请劳烦联系(微信:15227013954)立刻删除,谢谢!事件