最近只是一时兴起,觉得无聊,正好要到买票的时候,写了这个一系列文章,首先是对自己这些年来的工作经验的总结,其次是把分布式事务性系统的设计思想进行分析和整理,最后也就是和想集大家的智慧,讨论系统的设计。我不是铁道部的工程师,我只是一家互联网金融类公司的屌丝工程师,级别不高,能力也一般,就是喜欢技术而已。
在第二篇文章里面,重点分析了余票库的整体设计,我看到有的评论说了几点,现在整理一下
1 为什么要用悲观锁
为什么要用锁,由于之前是做金融系统,对数据的一致性要求很高。铁道部的出票操作要保证数据一致性,所以必须在获取余票的情况下锁定余票记录,否则会导致并发问题,多出票。如果是站票还无所谓,如果是卧铺咋办,一个席位,两张票。这个操作和帐户扣款一样,考虑下面的例子:
这里没有锁,导致多出票,这个只是两个线程,假设有10个线程,则多出10张票
考虑有锁的情况:
2 系统能否承受悲观锁
首先,我们需要看到什么情况下锁,以及锁持续时间,以及请求的并发数来进行分析。大多数情况下,铁道部购票系统承受的并发量都不会太大,除了节假日,主要是国庆节和春节。及时在国庆节和春节的情况下,我们假设每秒钟1000个请求去买座Z27的20121001的座票,那么锁记录持续的时间是多长了
分两种情况
a 有票:业务逻辑完成,释放锁
假设一般坐票有1000张,那么会有1000次锁业务操作,假设业务持续锁时间0.1秒(这个需要实际去进行压力测试),一秒钟处理10条记录,需要2分钟才能消化。而且这两分钟内oracle连接也被占用,后续的请求排队,系统缓慢,然后假死。这里面我们可以设置一个值,假设超过5秒,还没有办法获取到锁,自动释放连接,数据库返回错误,客户端可以选择重试查询票数或者报错给客户,当然,这个涉及到客户体验,如果获取不到锁,基本上可以告知客户票已经售完。
考虑到如果余票库有10个,那么就可以分摊一秒钟就可以100请求。这个具体还是,不过以上只是假设,需要有实际的数据以及压力测试要测试一下性能。
b 无票:直接释放锁
基本上非常快,不会占用资源。在下一个查询周期就不会在锁定记录,因为直接在缓存出就排除了。
所以个人认为使用悲观锁不会存在太大的问题,只要库设计的合理,锁超时时间设计的合理,对请求进行有限的控制,是可以支持的,当然,这个要求实际去检测。
3 不需要锁,只需要一台应用服务器启动线程处理购票,也不需要应用集群?
那可以想象一下,所有的购票请求都会同时请求到那一台处理购票的服务器上,假设这个每秒钟处理1000请求(这个据我所知已经算高了)基本上高峰期几秒种的数据就把服务压挂了。并且这个是单点,一旦这一台服务器挂了,整个系统瘫痪。
4 余票数据可以放在内存中吗?
放在内存,也就是放在内存缓存里面,这个问题有以下几个?
1 缓存故障
当缓存出现故障的时候,怎么办?当然可以缓存集群,切换到另外一台缓存服务器,但是余票的数据如何同步?两者不一致怎么办
2 系统发布
当系统发布的时候怎么解决缓存中的数据库问题,当然可以先把请求处理完成,然后在发布。或者发布之前,把缓存的数据同步到数据库中,这样也是可以的。但是设计上太复杂。
3 极端情况
硬件故障
操作系统故障
机房断电
这些故障都会导致内存数据丢失,余票数据都丢失了
我就知道我所在的公司遇到的变态的情况如下:
机房无故断电,网卡故障,磁盘写入失败,
经常遇到的情况是:jvm crash,内存泄漏,这些会导致放在内存的数据比较危险。
还有领域驱动设计和数据库设计两者没有必要联系。领域驱动是解决复杂业务领域的时候用的一种设计思想,自己在这方面开发比较多,和数据库这一层设计没有关系
对于性能来说,当然可以把所有的数据都放在内存里面,这样的处理效率最高,但是内存的数据就易丢失的数据。设计一个系统架构最大的问题就是权衡利弊。对于火车票系统,在出票的流程上,相当于金融系统的转账,余票相当于用户的余额,要保证最高优先级的安全性和一致性。这个性能相比,优先级要高。
谈谈对架构的认识
这里在说说谈谈自己对架构的认识吧,架构不是说就只管系统的性能,架构是全局观。涉及到安全性,可用性,性能,数据一致性,可扩展性等等。每个系统的应用特点不一样,所以思考的重点不一样。金融类系统对可用性和安全性数据一致性要求非常高,不能有任何的妥协,宁可牺牲性能,这就是为什么银行喜欢用IBM的产品。新浪这样的网站,性能很重要,但是如果丢失的用户少量数据,无所谓,所以数据可以放在内存里面,甚至可以用nodejs这样的新技术来实现。铁道部这样的国家级系统,可用性一定是在第一位的,其次是数据一致性,然后才是性能。就扯这么多,每个人的观点不一样,但是架构就是这么虚的东东。关系型数据库目前证明还是最可靠的,你能想象金融类帐务系统用的是NoSql这样的对事务要求不高的数据库吗?所以关系型适合在购票这样的核心应用。其他的库可以用NoSql来实现,比如会员库,没有问题。
渠道购票分配
上一篇文章提到了这个渠道订票的需求,目前有两种方式进行数据库层面的设计,第一种是各个渠道的数据切割开,互补影响,也就是说我网络订票不影响窗口售票,我想这个需求还是比较合理的,有限保证窗口票,那么可以把数据库水平分割,按照渠道来设计
这种设计的最大好处,就是各个渠道订票完全是分割开的,互相都不影响,但是这样最大的问题就是事先算好每个渠道的票的配合,比如窗口100张,网络20张,代售200张,电话30张。非常不灵活,比如假设我窗口还有票,但是窗口没有人买,怎么办,那只能其他渠道来卖。这样也比较浪费,毕竟大多数情况下,铁道部的购票还是系统还不是那么繁忙的。
另外一种设计就是在渠道层做流量控制,让流量控制这一层负责票数分配
这种设计方式就是比较考验渠道层,渠道层需要做流量控制,这样分配的最大问题,就是分配均匀,所以很好的算法。但是数据库层统一了,可扩展性比较强。容易进行扩容,也不会造成硬件的浪费。
两种设计方式体现的思想不一样,各有利弊。不过从更倾向于第二种设计方式,比较灵活。而且渠道流量这一层本身就必须的,这一层可以设计的比较厚,而且可以大量使用缓存或者NoSql。
余票库的再分析
1 余票库的分库策略
昨天讨论余票库分库策略,想起和一个铁路工程师聊起,再想想自己电话订票的时候,可以发现更好的分库方式。下面是从百度百科搜到的铁道部的组织架构:
铁路局是中国铁路管理体制的特色产物,是中国铁路四级(现在为三级)体制的重要组成部分。中国目前有18个铁路局(公司),分别是:哈尔滨、沈阳、北京、呼和浩特、郑州、济南、上海、南昌、广铁集团、柳州(2007年已搬迁至南宁)、成都、昆明、兰州、乌鲁木齐、青藏铁路公司、太原、西安、武汉。 |
铁道部下面分为18个铁路局,我估计每个铁路局负责的车次不一样,那么分库就可以按照铁路局分库,这样就可以分为18个数据库。但是考虑到余票库的实际数据量并不大,只是高并发,我们没有必要分成18个库。但是我估计由于利益分配,这个18个铁路局应该每个铁路局都有自己的数据中心。但是我们这是纯技术层面的分析,先不考虑利益和实际情况。我们可以按照铁局路分库,但是前提是负责卖票的车次都不一样,否则一个车次,分到两个库里面系统会变的很复杂。假设范围分为四个,东南西北,比如(哈尔并,沈阳,北京,呼和浩特)一个库。这样我们就有一个简单的模型了,那么在梳理一下一次典型的购票数据流
其中虚线圈起来的部分是要保持数据一致性。
简单的说就是 余票减少一张,车票库待支付记录就会多一条;车票支付成功,车票状态要变更,支付数据完成,短信要发送,当然这个要看你对一致性要求有多高,车票购票成功,短信发送不一定要成功。但是车票支付成功,支付数据总要有,并且是成功的吧。这个要进行对账的,否则就是一笔糊涂账。据说铁道部还会建立会员营销系统,那么越来越多的功能就会更复杂,比如支付成功,积分增加一百,积分以后可以进行车票的支付等等。不过这个不是核心,如果能继续写,在分析吧。
至于在分布式环境下如何保持数据一致性,这个我会专门写几篇文章来进行分析,我觉得这个是比较有价值的。
2 如何确定余票
这个是第二篇博文中我没有写的,我觉得这个是最复杂的,现在继续分析。
确定余票的因素:日期,车次(假设动车,普通车次不重复),出发站,到达站,席位
其他三个很容易从数据库中获取,这里不说了,主要是出发站,到达站两个维度。
如果通过其实出发站和到达站进行查询,首先我们根据车站确认可以通的车次,可以简化为对N个车次查询,在根据出发站,到达站进行分析
假设 车次Z27,坐票,20121001,中途经过5站(A,B,C,D,E),共有100张,简化记录为(A->E) = 100
假设用户U1买了从A->E的车票一张,那么还剩下99张, 就是 (A->E) 99
假设用户U2买了从A->C,那么 (A->E) = 98 ,(A->C)=98 (C->E) = 99
假设用户U3买了从C->D, 那么 (A->E) =97 ,(A->C) = 98 , (C->E) = 98 (C->D) = 98 , (D->E) 99
看来越来越复杂,但是我们发现这个和二叉树有关,看一下下面的图,不断构造一个树的过程,而且判断票和也查找节点有关,找到之后此节点票数-1,所有的父节点票数-1。我算法不好,只能想到这个算法了,不知道有没有更好的算法,这个树不需要实时更新,放在内存里面。这样就方便查询了。
整个架构的基本模型
把第一篇和第二票文章整理的内容在综合整理以下,在应用在丰富以下,可以搭建一个简单的分布式系统的应用原型,透过这个原型,我们在不断的完善系统
目前铁道部可能的架构模型
最后看到铁道部的组织架构,感觉铁道部的系统架构可能大致是这样的:随便乱太猜想的,下面一个简单的模型,实际情况远远比这个复杂。
由于数据中心是分布的,所以系统很慢。因为数据这一层路由,都是远程调用啊。同时铁道部的应用应该也是分布部署的,由于数据中心不集中,花在系统间通信的成本太高。感觉这个可能是铁道部内部的政治格局导致的,以前的银行都是这么搞的,每个省都有自己的一套系统。
后续
写了这么多,先休息一段时间吧,后续有时间在继续分析这个系统,今天也恰好看到一个新闻:
据介绍,12306互联网购票系统是基于中国铁路客票发售和预订系统(简称客票系统)这一核心系统构建的。客票系统在10余年的运营过程中先后完成6次升级:1.0版本实现了计算机售票取代人工硬板票,2.0版本实现了区域级联网,3.0版本实现了全国联网售票,4.0版本实现了与清分清算系统的对接,5.0版本实现了席位复用和共用,5.2版本实现了实名制售票、电子客票和电子支付。
由于2.0版本是区域级别售票,3.0实现全国联网售票,但是我估计所谓实现全国联网售票,因为是在2.0的基础上通过适配和路由搭建的,铁道部应该还没有统一的数据中心,数据应该是各个铁路局控制的。
4.0版本主要做清分系统,这个就是内部的核算系统,应该算是铁道部内部最为复杂的一个系统了,比如我卖了1000张票,应该得多少钱,应付给代售点和合作商户做多少钱。
5.0可能就是上面说的同一个位置,由于区域段不同,可以买两张以上的票,只要铁路段不重复,也就是我说的余票树模型。
5.2就是实名制和增加了电子支付和电子客票,这个应该稍微简单一点,只是增加了一种支付工具和验票手段。所以版本号就没有怎么升级。
看看新的客票系统的愿景;
铁道部透露,新一代客票系统实现了四方面创新。一是服务模式创新,系统支撑包括票务服务、旅行服务等各种延伸服务在内的预订业务。二是营销理念创新,对铁路列车开行、运力调整、票价优惠等提出合理化建议;制定铁路客户常旅客计划,建立旅客积分、奖励制度。三是管理手段创新,满足淡旺季不同客流特点下的售票组织需要。四是技术架构创新,研发高性能核心交易平台。
从业务角度来看,
1 铁道部想建立客户模型,目前铁道部还没有客户模型,毕竟实名制刚开始,区分优质客户以及黑名单。未来可能你座的越多,可能走优先贵宾通道上车等等
2 建立积分模型,这个主要是用来营销,和航空和银行一样,估计就是换礼品什么的
3 技术架构创新,我觉得这个是最重要的,现在的架构可能因为历史的原因,比较弱。
4 高性能核心交易平台:这个是必须的,类似淘宝那样的交易平台。从全球性来看,ebay的交易平台比较牛逼,不过淘宝最近的交易数据不知道超过ebay没有。这个交易平台未来会不会开放,也是一个想想的空间。
未来还可以想想的是,一旦铁道部建立的会员模型,更有可能往金融上面去靠拢,建立自己的账户模型,推出更多的支付方式,比如预存款,联名卡等等。铁道部相当于拥有最多实名制会员的公司,想象空间很大。但是前提是系统要做得好,别再被人骂。
爱公司
出处:http://www.cnblogs.com/aigongsi/
本人版权归作者和博客园所有,欢迎转载,转载请注明出处。