五千年(敝帚自珍)

主题:【原创】无责任推测12306网站遇到的麻烦 -- 代码ABC

共:💬135 🌺246
全看树展主题 · 分页首页 上页
/ 9
下页 末页
家园 【原创】无责任推测12306网站遇到的麻烦

首先声明,这是技术贴,也许有点阴阳怪气,但不洗地,咱纯粹看戏。

其次本人无任何内幕信息,也没有参与铁路部门任何项目,所以许多假设仅仅是假设。如有雷同那是运气,如果与事实不符也很正常。

我的假设主要基于两个数字:一个是新闻里说的铁路春运期间,每天平均运送旅客7000万人次,另一个是每列客车可以载1000人上路。另一个假定是12306网站和铁路的内部票务系统无直接的数据连接——这个假定我认为是靠谱的,因为窗口售票并不受网站瘫痪影响,证明铁路内部系统运作正常。因此进一步推断:铁路部门每天给会将一张可售票总表交给网站,里面大致应包含时间、车次、各车次可受票的种类和数量。12306网站则定时提供一张汇总表报告售票情况。

那么我们来分析一下设计这个网上售票系统所需要考虑的问题。

相信通过这几天的表现我们知道整个系统的核心在于用户的使用压力上,其实网站的功能并不复杂,核心的功能(按使用频度顺序)就是票务查询、订票、用户身份验证和注册,另外就是一些杂七杂八的辅助功能,如:订单管理、个人资料修改等等。数据库(假设使用数据库解决方案)内的数据主要就是用户数据、车票数据和订单数据。

我们先看最简单的用户数据部分,这部分相关的操作是注册、登录,当然在订票的时候也会用到,但一方面在订票数据里这应该只是一个外键,另一方面可以暂时缓存在用户的交易会话中,所以实际会操作到这个数据的就是上述两个动作。注册是一次性的,登录则不是,因此查询动作会多一些。但无论如何这个数据表(假设设计成一张表)的查询非常简单,优化也很方便。所以这个表虽然很大(目前数据超过1000万),但乱子不会出现在这个地方。其实12305的登录动作是很快的,那个人数太多只是一个临时措施免得太多人同时进行交易而已,留意一下只要能登录进去那个登录页面反应其实不慢就是这个道理。

我们再看订单数据,这个数据也会很大,因为需要存放每张票的详细信息,如果每天有四分之一的票是通过网络出售的那么这个数据是每天增加近2000万条。不过这个数据表(假设还是一张表)主要的动作就是插入和修改,几乎不会查询。所以优化也不困难。这里设计的麻烦在在线支付和相关的车票数据锁定的问题上。当我们选好票进入支付环节的时候,系统必须首先将选好的票锁定起来,也就是余票查询将会扣减相应的票数。这个操作的复杂性比登录要高就在于这点,需要同时修改两个地方:车票数据和订单数据,而且必须是同时的。在数据库设计上这叫要求事务的完整性。通常这种操作需要通过锁定一些数据记录来完成,也就是当系统修改车票数据和生成订单期间,车票数据和订单相关的车次查询将被暂时中止。这样订票就影响了查询性能。反过来也一样,查询的时候也不允许生成相应车次的订单。其实这有点扯,系统真正要防止的是当只有一张票的时候,如果有多个人同时订票那么只能有一个人成功,虽然他们之前都查到有一张余票。所以实际上需要保证的是一个时间只有一个人可以修改车票信息。因此,为提高性能我们可以放宽对查询准确性的要求,即查询操作不锁定车票信息,不理会当前有多少人在订票。反正大家心里有准备最后几张票都是抢的。当然锁定范围肯定不是整个数据表,而是订单相关的车次记录。也许12306的第一个瓶颈问题先发生在这里吧。因为如果查询和订票会相互影响的话,那么怎么优化都有问题。我猜他们后来搞出一个30分钟更新的数据就是为了提供一个单独的车票数据表供查询用,这样大致上隔离的订票的查询之间的冲突。也许他们忘记数据库事务控制中锁的作用吧。我这样大而化之的分析肯定有很多错误,不过这是一个所有DBA和程序员都应该知道的基本常识。接下来的车票数据可能会更复杂,也许是当前即使使用用一个单独的数据库来进行余票查询也非常慢的原因吧。

车票数据我的估计是这样的,每天有7万个车次(7000万/1000)。靠谱吗?我估计数量级大概是靠谱的。谁能给更准确的数字?请不吝赐教,谢谢。车次的问题在于除非是直达车,否则一个车次可以分开好几段来售票。那么7万车次就可能不只7万条记录,这里还会牵涉到几个让一般程序员感到棘手的算法问题。算法?拜托都还给老师了吧。实际上怎么存放车票信息将牵涉到具体如何实现余票查询的算法。举个例子:有一趟车从广州开往北京,经停南昌、株洲(铁路知识一片空白,地名乱填的,别拍砖哈)有10张卧铺票。甲买了一张广州到株洲的,这时株洲到北京还剩10张,广州到北京就剩下9张了。实际还需要扩展一下,如果有人查南昌到株洲呢?还是9张,广州到株洲呢——9张。经停站越多,起始站和终点站的组合就越多,这个算法复杂性就在这里。设计这种信息的存放就是一个挑战。如果列车的售票模式是允许这样的话,我想我知道他们的麻烦在哪了。

首先最笨的方法是,每个车次只存一条数据记录,然后关联另一张节点表(存放经停站点用)。这样查询余票的时候需要联合这两张表(还可能有计算)并汇总已有订票记录表。这种存放方式的特点在于数据冗余少。但是查询开销极大,因为需要访问一个庞大的订票数据表(每天2000万的增长)。如果哪个坚持数据库范式的DBA这么干,那么一定就悲剧鸟。

不过我们可以从上面开始优化,其中可以将订票数据表生成一张汇总表。(毕竟我们只需要知道已经出售的票数,而不需要知道哪张票是谁买的)只记录车次、起始站、终点站的售票张数。这样可以把每天2000万的数据缩减为每天差不多70万的数据,具体估算如下:假设一次列车平均5个站,那么起止组合就平均有10左右(不严格)车次。那么每天汇总就是7万x10,约70万条数据。几天下来就是几百万。不过由于需要联合多个表查询,其处理集合的数量级还是上千万甚至上亿。这样对于高负荷网站来说还是有点受不了。而且汇总数据和订票数据相关,不能实时生成。也许这能解释为什么余票数据改成30分钟一次。

再进一步,其实我们可以把车次数据直接拆开了存放,也就是每个车次直接存放为各种起止点的组合,而且直接把余票数据放进来。这样查询的时候一张表就搞定了。每天增加70万数据,12天的票也在1000万的量级。这样性能好点的服务器应该就可以解决问题了。麻烦只在于这时候订票操作需要修改多个数据。但是订票操作应该远远小于查询操作。整体来看系统性能还是可以提升不少的。

实际上,这个问题有很多解决方法,由于手头没有详细的车次数据、铁路局的节点数据、用户查询习惯等支持。所以没有什么建议,但有一个直觉就是纯数据库解决方法可能不是最优解。也许转换成有向图直接在内存中计算会更快。而且还能增加转车计算的功能。没有数据再掰下去就太YY了。鞠躬,下台。

通宝推:雪君,文字君子,铁手,muilho,
家园 所以单纯技术方案不能解决供求矛盾

网上订票方便且快捷,大大降低购票成本。从前购买车票除了票价本身,还要付出购票过程所需成本,这包括前往售票点交通、排队需要的时间等等。但是由于购票者群体的收入结构与知识技能的差异,网上售票降低了购票成本,在实际上掠夺了低收入低技能购票者的可及资源,扩大了社会分配不公。这个问题的根源显然是铁路客运能力仍然严重不足而形成的尖锐供求矛盾。而要避免市场供求矛盾扩散成为社会矛盾,最直接的办法仍然是运用市场调节手段,而不是网上订票这样的纯技术方案。我的建议是车票的多路分销和分级定价,比如售票窗、网络直销、旅馆酒店和旅行社包销这样多路分销,购票成本越低的途径,车票价格的就越高。比如住在酒店的旅客可以到前台随时拿票,但那就是天价票;网络订票方便?打入交通成本和排队时间,票价较售票窗高很多。窗口票价最低,但请耐心排队。其中的关键当然是各个价格段的目标精确性。

家园 写的不错

有些东西外行可能看不懂,比如什么是外键,什么是联合。

至于数据库设计,选什么做key,那就更复杂了,外行恐怕也不关心,内行嘛,水平问题。

这个东西放在数据库课里当个小项目倒是挺不错。

家园 差不多吧

送花赞扬 关闭

送花成功。有效送花赞扬。感谢:作者获得通宝一枚。恭喜:你意外获得 8 铢钱。1通宝=16铢

参数变化,作者,声望:1;铢钱:16。你,乐善:1;铢钱:7。本帖花:1

讨论一下:

支付是否可以采用手机?只要手机话费有足够的余额就可以完成购票。支付时,余额足够时,12306向手机发送一个确认短信,回复Y,即完成购票;余额不足票款时,12306直接发送一个余额不足的短信通知机主即可。

家园 后台好象是ORACLE?

数据量很大的话传统的数据库如DB2、ORACLE之类的肯定力不从心,用NETEZZA之类的数据库比较好。

首先最笨的方法是,每个车次只存一条数据记录,然后关联另一张节点表(存放经停站点用)。这样查询余票的时候需要联合这两张表(还可能有计算)并汇总已有订票记录表。这种存放方式的特点在于数据冗余少。但是查询开销极大,因为需要访问一个庞大的订票数据表(每天2000万的增长)。如果哪个坚持数据库范式的DBA这么干,那么一定就悲剧鸟。

每天2000万增长其实还好,可以将日期设为PARTITION KEY进行分割,相当于把数据量固定在2000万。估计主要瓶颈还是你后面说的,不同起止点的组合查询,

家园 既然是窗口和网站并行

从求稳的角度来说

恐怕最底层库还是沿用原来窗口系统的来保证对接

对于逻辑结构大动的难度太大了

而且,票数并没有增加,所以和原系统相比,数据量并没有增加

主要的问题应该出在访问量过大上,这个东西的瓶颈只怕不在数据库上

另外,关于锁定的问题,是不是直接当作已出票处理,然后半小时后REVIEW一下,发现尚未到帐就直接释放掉就行,不用搞锁定这么复杂的东西

家园 不会用原有的系统

不然网站就会拖累窗口系统。而且我估计票源还是象以往那样分发,即网站是一个超级代理点,这样铁路系统的业务不需要做太大的改动,风险也小。所以网站是一个相对独立的系统,怎么设计都行。

访问量过大,瓶颈90%以上是数据库设计问题。现在的网站对于静态内容的访问处理效率是很高的,所以不会是带宽问题,即使是带宽问题我觉得以铁路的地位,电信的资源不难调度。

我说的锁定不是锁住车票数据等支付,而是确保多人竞争的时候不会出现冲突或者超卖的情况。这个锁定时间是很短的(理论上)。这个锁定是数据库内部的一个基本功能而已。一般来说为保证数据的一致性,即使是查询动作在数据库内部也是有锁的。

家园 这个数据量还没超出Oracle的能力

事实上,我觉得连总是躺着中枪的微软的SQL Server也可以,同时我认为那个组合问题用数据库查询来实现,效率不会很高。不认真分析的话用什么数据库都会出现相同的问题。

个人觉得也许数据库用来保存状态,直接用适当的数据结构和算法来搜索可能会更好。不过我没有这个系统的任何资料,所以这些都是纸上谈兵而已。

家园 不太了解原来的铁路系统是怎么运作的

心里总觉得这种分站式的售票,一条线上的一个座或者一个床根据用户需求可能分成两段甚至三段卖,采用分发的模式不是特别对路.必须有个总控来做数据整合才能避免浪费.(不过实际坐车的经验也确实是列车上工作人员对于票是否已售出完全不知道,想必是真没有数据总控)

锁定那里的确我理解错了

家园 电信分一口

咱铁道部会不会同意?

家园 远没有7000万人次/天那么多

2011年10月1号的数据:

10月1日,全国铁路运输旅客892.8万人,同比增加63.5万人,增长7.7%,创铁路单日发送旅客最高纪录。全国铁路当日加开临客310列。

最近几天的数据:

1月9日,全国铁路发送人数524.6万人次,同比增加28.2万人次,同比增长5.7%。1月10日,铁路部门计划加开旅客列车548列。

1月10日,全国铁路共发送旅客534.3万人次,同比增加17.4万人次,同比增长3.4%。
1月11日,全国铁路发送旅客551.6万人次,同比增加23.8万人次,增长4.5%。当天,铁路部门加开旅客列车570列。

铁道部官员如是说:

铁道部部长盛光祖说,2012年春运客运总量将大幅增长。春运40天,全国铁路将发送旅客2.35亿人次,同比增长6.1%,日均588万人次
2012年春运期间,铁路将增开图定旅客列车131对,总量将达到2064对。按同口径测算,全路春运图定客车节前能力达到479.8万、节后能力达到468.8万,同平时相比增长约25%。

家园 我曾经2次尝试过网上购票

第一次登陆成功,然后查询余票这一块速度是相当快的,但是下订单时候被告知连接数过多失败。第二次登录直接失败,被告知连接数过多。

由此来看我不认为瓶颈在数据库,而在中间件,是中间件活动连接数被撑爆了。因为无论你下单还是登陆都需要通过中间件连接db。但是这并不意味着中间件是问题的根源。

我认为中间件的问题是由于用户在站内停留时间过长造成的,大量用户尝试通过中间件连接db,导致任何人的短暂释放连接都会使其下一次尝试失败。

那么什么原因导致用户在站内停留时间过长?很可能是第三方接口,我认为最大的可能是网银。用户在余票查询完毕,下完单支付时会被直接跳转到网银,这意味要么其原有db连接被cache,要么需要在支付完毕后重新连接db,无论哪种方式在当前这种网银支付速度下都是难以接受的。

而从售票系统的查询余票速度很快这一点来看,铁路售票系统的数据库逻辑设计是相当成熟的,我不认为其逻辑设计会造成性能问题。但是订单支付需要直接调用网银界面,那个不仅是慢的问题,成功支付率都不能保证,如此就会直接导致平均每个用户在站内停留时间过长,进而导致其在中间件的db连接失效,最终订票失败。

支付宝的速度快是因为大多数情况下你的支付宝账号里有足够余额,这样就没有第三方接口的问题。所以要解决平均用户停留时间过长的问题要么开发个支付宝,要么用马云的产品。

家园 我的经验不一样

余票查询不需要登录,我在低谷时间查询是非常快的。但是在高峰期(登录不上的时候)就几乎停顿。这是在1月6日和7日的测试。

我没实验过支付,但我做过支付的网站。中间件不是这样设计的,首先中间件不需要cache连接,数据库访问通过连接池进行,这样每次业务连接数据库的时间会很短。按每天10亿次点击算,大致上连接池有3000-5000就可以满足数据库连接要求。但是这要求每次操作都在1秒的数量级上完成。其次,用户做支付动作的时候是不需要中间件保持活动的,支付完成后支付接口通常有两种方式通知网站支付结果:URL跳转和TCP通知,这时候再通过中间件访问数据库进行交易确认。每笔交易通过事先向支付网关提交的支付单号对应起来。因此订单支付动作对于本网站来说就是两次查询:1、生成订单,并提交支付;2、确认支付。1和2之间可以间隔任意长的时间,不会对本网的中间件有任何影响。而且和用什么支付手段完全无关,支付宝不解决问题。

如果按每个用户都时刻连着数据库来设计的话,这种网站很快就会完蛋。我之所以认为查询有问题,原因就是看到那个30分钟才更新的余票数据的变动。如果查询没问题他们不需要这样修改,这类型的修改大多是因为查询开销太大,需要构造一个汇总表来事先汇总一些中间结果。由此也可以推断出原本的查询是关联了多张表的复合查询。

家园 谢谢,那我的估计偏大了一个数量级

日,我发现我用了总客运量了。算了,不改了。

家园 有些没有你想的复杂

“举个例子:有一趟车从广州开往北京,经停南昌、株洲(铁路知识一片空白,地名乱填的,别拍砖哈)有10张卧铺票。甲买了一张广州到株洲的,这时株洲到北京还剩10张,广州到北京就剩下9张了。实际还需要扩展一下,如果有人查南昌到株洲呢?还是9张,广州到株洲呢——9张。经停站越多,起始站和终点站的组合就越多,这个算法复杂性就在这里。”

长、短途客票的分配应该是客运计划部门早就做好了。

就是说铁路内网push给12306车票数据库的每一条记录对应一张车票,一张“发站&&到站&&车次&&日期&&座号”已经确定的车票

12306没有动态生成车票的能力

全看树展主题 · 分页首页 上页
/ 9
下页 末页


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河