《今天你买到票了吗?——从铁道部12306.cn站点漫谈电子商务站点的“海量事务快速处理”系统》

首发地址: http://bbs.hpx-party.org/thread-8488-1-1.html 。一万四千字电子商务站点干货贴,贯穿产品、研发、运维三大领域,没有耐心读完的读者慎入,另外,本文不是教你怎样订到车票的。

相关文章:

不能简单套用“实物电商系统”对“大型票务系统”做需求分析

“大型票务系统”和“实物电商系统”在不能提供商品(服务)时给消费者带来的影响有巨大差异

“大型票务系统”和“实物电商系统”的系统边界之间的差别与联系

“大型票务系统”和“实物电商系统”按系统边界分析各种业务形式

“大型票务系统”和“实物电商系统”在支付方面的差别和联系

“大型票务系统”和“实物电商系统”在恶意订单方面的差别与联系

大型票务系统中username和password的安全性问题

大型票务系统自身特点所引入的安全性问题

“大型票务系统”和“实物电商系统”在“库存”计算方面的巨大差异

“大型票务系统”和“实物电商系统”在接入管理方面的差异

“大型票务系统”和“实物电商系统”在和企业其它部门关系的联系和误区

“大型票务系统”和“实物电商系统”和企业其它部门的依赖关系对项目管理的影响

“大型票务系统”和“实物电商系统”的数据库选型

“大型票务系统”中对机器恶意訪问的处理——验证码

“大型票务系统”中地恶意訪问的处理——“接入管理”部分

宁夏卫视《第一財经》《首席评论》2012年1月12日《被“网”住的铁道部》。视频请猛击 http://v.youku.com/v_show/id_XMzQyMjg0ODky.html



Flash请直接观看 http://player.youku.com/player.php/sid/XMzQyMjg0ODky/v.swf

採訪新闻上了新浪的首页 http://tech.sina.com.cn/i/2012-01-11/00416623854.shtml

採訪新闻上了腾讯的首页 http://finance.qq.com/a/20120111/000728.htm

我在新浪微博上转发了几条已知的预订火车票的技术漏洞 http://www.weibo.com/443089607

文章比較长,成文仓促,结构的确不晴晰,向各位读者致歉,写一个本文导读吧。整篇文章论述的就是“海量事务快速处理”的经验和误区。

第一部分论述“海量事务快速处理”现阶段没有通用解决方式,尝试通用解决方式就是误区。

第二部分解说算法问题、安全问题经验,以及一些误区。

第三部分解说电子商务站点的核心交易系统怎样随着站点的发展而演进,分成了三个发展阶段,发展过程中的一些经验和误区。

另外,详细的需求的确不在本文讨论之列,望各位读者海涵。

今天你买到票了吗?

12306.cn是中国铁路客户服务中心站点。作为铁道部唯一授权的火车票订票站点,在2012年的春运前夕差点儿成为了一个家喻户晓的站点。由于从今年起,每一个人都能够通过这个站点以实名制的方式预订火车票,和家人团聚,共度一个祥和幸福美满团圆的春节。与此同一时候,12306.cn站点也被舆论推到了风口浪尖,这并非由于12306.cn站点满足了每一位用户回家的愿望而获得了赞誉。而是在互联网已经開始进入每一个人日常生活的时代,12306.cn站点的界面丑陋、UI(用户界面)粗糙,不夸张的说,比十年前的站点还逊色。当然,作为一个功能性站点,用户也不会苛求界面的美观,UI的精致,可是让每一位用户都不可接受的是,12306.cn站点的server不稳定,屡屡瘫痪。笔者非常荣幸应CSDN之邀撰文对本次事件做一些分析。

作为政府部门唯一授权的站点,用户没实用脚投票选择离开的权力。刚刚过去的2011年是中国电子商务站点遍地开花的一年,中国的每家电子商务站点都在比拼烧钱打广告,期望获得更高的全球Alexa排名,期望获得更高的订单数量、销售数量、用户转化率。按着笔者了解的情况,在2011年,电子商务站点每IP的广告费用平均约为0.4元,转化为注冊用户时的每注冊用户的广告费用平均约为4元,转化为实际销售的每一个首次销售订单的广告费用平均约为40元,也就是说一位通过电子商务站点广告而訪问电子商务站点产生的首次购买,假设利润低于40元的话,那么就是在赔钱。

假设从电子商务的角度来看12306.cn站点的话,情况正好相反,没做一分钱广告,只几天的时间Alexa的排名飙升至260,这不是中国站点的排名,是全球站点的排名,并且还有不断上升的趋势,日订单量非常可能成为全国第一,销售额都是可预知的,由于每列火车中每节车厢的每一个座位都会卖出去,转化率达到了前无古人后无来者的100%,由于全部的注冊用户,都是想买车票的,只能是他买不到,不可能是他不买。媒体普遍戏称12306.cn站点是中国、仍至全球有史以来最牛的电子商务站点。

作为12306.cn主管部门——铁道部,从建国之初就有一个众所周知的绰号“铁老大”。可是作为12306.cn站点的制作单位——中国铁道科学研究院(简称铁科研),从一家仅为业内人所熟知的科研单位,只几天的时间就受到了广泛的关注,由于每一位在订票时屡屡遇到server不稳定、甚至瘫痪的用户,特别是前一秒中还看到有票,由于进不去订票界面,后一秒钟就看到无票的用户,都会对这个站点的制作者产生好奇,到底是谁做出这么一个站点?浏览到12306.cn站点页面的页脚就会看到“中国铁道科学研究院”的名字。接下来笔者将分析研发一下类似于12306.cn站点将会遇到哪些问题,以及怎样通过现有的技术手段解决这些问题,并探讨在一般的电子商务站点中怎样处理同类问题。

第一部分

在线处理的四种类型

互联站点除了浏览器端的展示外,在server端的程序归属于“在线处理”的范畴。一般来说“在线处理”能够分成“在线事务处理”(OLTP,OnLine Transaction Process)和“在线分析处理”(OLAP,OnLine Analytics Process)两类处理方式。事务(Transaction)这个概念来源于数据库,在数据库中事务(Transaction)是訪问并可能更新数据库中各种数据项的一个程序运行单元(unit)。

随着技术的发展,事务已经不只局限在数据库领域。现阶段,一般用两种角度来描写叙述一个事务,一个是从外部的角度,事务是恢复和并发控制的基本单位。还有一利是内部的角度:事务,是由一系列不可切割而且不可错序的动作组成。尽管理解事务的角度随着技术的发展不断演讲,可是用于描写叙述并评估事务的属性保持不变,一个事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。

  • 原子性(Atomicity)。一个事务是一个不可切割的工作单位,事务中包含的诸操作要么都做,要么都不做。
  • 一致性(Consistency)。事务必须是使数据库从一个一致性状态变到还有一个一致性状态。一致性与原子性是密切相关的。
  • 隔离性(Isolation)。一个事务的运行不能被其它事务干扰。即一个事务内部的操作及使用的数据对并发的其它事务是隔离的,并发运行的各个事务之间不能互相干扰。
  • 持久性(Durability)。持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其有不论什么影响。

以12306.cn为例,为避免“黄牛”,购票系统有一个业务逻辑:一个有效身份证件同一乘车日期同一车次限购一张车票。因此购买一张车票能够简化为包括四个操作:

  1. 推断同一乘车日期同一车次是否有未预订的空余座位
  2. 推断这个有效身份证是否已购买过同一乘车日期同一车次的车票
  3. 车票上标注的座位标记为已预订
  4. 假设没有购买过,则该身份证预订一张车票

原子性是指用户提交一次购买的时候,要么该车票上标注的座位标记为已预订而且该身份证预订一张车票;要么没有标记该车票上标注的座位已预订而且该身份证预订失败。也就是第3个操作和第4个操作要么都做,要么都不做。不同意没有标记该车票上标注的座位已预订,可是该身份证预订了一张标注为这个座位的车票,或者有某一个座位被标记为预订可是该身份证预订失败。一致性也是指类似的含义。

隔离性是指如果有两个用户同一时候预订同一乘车日期同一车次的车票时,如果仅仅剩一张票了,那么第一个用户推断出有未预订的空余座位,那么他就能够成功预订车票。因此既便是第二个用户在第一个用户标记该座位已预订之前推断是否有未预订的空余座位,返回给他的结果也是没有空余座位。

持久性比較好理解,假设一个用户已成功预订了一个座位,那么其它用户就不能预订同一乘车日期同一车次的同一个座位了。

对于一般的电子商务站点来说,一次购买行为能够简化为四个操作:

  1. 推断该商品有库存
  2. 推断该用户的账户剩余金额
  3. 降低该用户的账户剩余金额
  4. 降低该商品的库存

这种四个操作组成了一次购买行为,也须要满足ACID属性,当然实际的场景要复杂得多。

一般来说,不具备ACID属性的在线处理都能够称之为OLAP,这不意味着OLAP比OLTP简单。通常所说的OLAP是指对一定数量的数据进行统计分析的操作,需到强大的计算资源支持。OLTP和OLAP是评估“在线处理”的一个角度,还有一个角度就是对于处理速度,也就是响应速度的要求,速度一般和数据量紧密关联,相对于海量快速处理而言,海量低速处理,或者少量快速处理就easy得多。通常把海量低速处理或者少量快速处理统称为低速处理。

这样对于在线处理的两个角度就构成了四种情况:海量事务快速处理,海量分析快速处理,事务低速处理,分析低速处理四种情况。

 

OLTP

OLAP

海量快速

海量事务快速处理

海量分析快速处理

低速

事务低速处理

分析低速处理

“海量事务快速处理”与其它在线处理的差别与联系

在这四种情况中,先讲一下分析低速处理。尽管分析低速处理看起来不要求速度,也不要求事务,可是在在线处理中被纳入到分析低速处理领域的问题通常是指复杂算法方面的处理,常见的有聚类分析,在本文中不再展开讨论。以下分别针对海量快速中的OLTP和OLAP的差别展开探讨,以及OLTP中的海量快速和低速展开探讨。

对于海量快速而言,由于涉及到相当规模的运算量,远远超过单台计算机的处理能力,须要多台机算机组成一个系统,对于外界来说就像是一个系统,这种方式被称为集群计算。集群计算除了用于解决运算量之外,同一时候也用于提高系统的可用性,当集群中的一台计算机出现问题时,还有一台计算机能够接替它的计算任务,从外界来看,集群相对单台计算机而言,可用性提高了。

在OLAP中,由于每一个在线处理过程之间都是相对独立的,不具有OLTP的ACID属性,因此使用集群计算处理分析任务时,一般的过程是把任务分发到多台计算机上,全部计算机返回计算结果后,再汇总结果返回给用户,这样的方式一般被称为快速并行计算。从字面意义上看,全部的计算任务都是并行处理的,之间没有太多的依赖关系。对于快速并行计算而言,仅仅要有足够的传输带宽、server够快、够贵、够多就能够解决所须要的计算量的要求。

这也是眼下“海量事务快速处理”混同于“海量分析快速处理”时常见的一种误解,觉得使用“快速并行计算”解决“海量分析快速处理”的方式能够用于解决“海理事务快速处理”,比方说使用单台server一秒钟能够处理一个交易,那么使用一万台server就能够一秒种处理一万个交易。或者说是仅仅要有足够的传输带宽、server够快、够贵、够多就能够解决“海量事务快速处理“的问题,这也是本文要澄清的关键问题之中的一个。这也是相当一部分电子商务站点从初期的低速处理时期向“海量快速”发展时遇到的最大技术瓶颈。

当然这并不意味着“海量分析快速处理”比“海量事务快速处理”easy,这是两个不同的方向,一个典型的样例就是搜索引擎,大型搜索引擎中的一次搜索行为并不会涉及ACID等等属性,可是仍须要将一次搜索任务分发给数百台或者很多其它的server,然后再合并这些server的搜索结果后返回给用户。在这方面典型的技术就是Google的MapReduce编程模型,眼下有Hadoop这个开源项目实现了Google的MapReduce模型。

“海量事务快速处理”从量变到质变

由于事务具有ACID等属性,因此“海量事务快速处理”和“海量分析快速处理”须要全然不同的技术路线,在国内,12306.cn并非第一个遭遇这个问题的站点,2008年奥运会的订票系统也遭遇过相同的窘境,仅仅是由于用户量相对少些,和大多数人的关系不密切而没有得到广泛的关注。当然假设数据量没有那么多,速度要求也没有那么高的话,也就是在“事务低速处理”领域中,眼下有着丰富的解决方式,眼下主流的数据库,特别是商业数据库都重点关注这一领域。

特别是从详细的需求来看,“事务低速处理”和“海量事务快速处理”仅仅是“量”上的差别,作为需求的“事务”本身并没有不论什么变化,因此一个简单的思维就是开发一个“事务低速处理”的站点,然后用“海量分析快速处理”的快速并行计算解决方式来解决“量”问题,可是结果并不如意,在辩证法中的三大定律之中的一个——量变引起质变,也相同适用于“海理事务快速处理”。近些年来,已经有非常多先行者尝试过努力,也积累了一定的经验和教训。

在通用领域中,既便是作为领头羊的IBM、HP等公司碰到这样的系统,都是仅仅卖硬件,绝不为软件系统承担不论什么风险和责任。说明确了,我挣硬件的钱,软件,你们谁不怕死的话谁就向前冲吧。多年来在通用领域毫无建树不意味着“海量事务快速处理”是不可解决的难题,在专用领域“海量事务快速处理”的发展就很引人注目,当中最典型的案例就是IBM为大型金融机构提供的IT解决方式。分析这个案例有助于拨开“海量事务快速处理”的重重迷雾。

在IBM的解决方式中,其核心就是S390大型机,笔者尽管没有接触过S390大型机,可是以前在十年前学习过AIX小型机,并取得了中级管理员证书。在这里不展开解说详细的小型机、大型机技术,整体而言,这些设备所表现出来的卓越运算能力,都是局限在某个特定的领域。也就是说,IBM为金融领域的特定场景“定制”了一套系统,这套系统在硬件、操作系统、数据库、中间件、应用程序整个流程的每一个环节都做了优化,消除了全部的瓶颈。

换句话说,尽管在这套系统中仍然能够区分出硬件、操作系统、数据库、中间件、应用程序。但已经和通用领域中的含义不同了。比方说S390上执行的数据库是DB2,事务中间件是CICS,在普通PC上也有着相应的DB2和CICS产品,但并非简单的移植,在S390上的这些软件通过和硬件、操作系统紧密结合从而表现出卓越的性能,换句话说,在普通PC上的DB2和CICS等产品不过提供给开发人员便于学习和开发而已,脱离了IBM的专用硬件,这些软件的表现明显欠佳。

通过S390的样例能够看出,在专用领域中,“海量事务快速处理”须要按实际的需求“定制”,眼下以及未来相当长的一段时期间不会出现通用的“海量事务快速处理”的解决方式,更不会出现对应的通用软件。当然一个现实的问题是,在非常多领域中,并不会都像金融业那么有钱来支撑硬件的研发,比方说电子商务领域,既不能像金融业那样来支撑硬件的研发,同一时候业务量又相当于一些小规模的银行。此时就须要考虑怎样在通用硬件上,借助于“定制”操作系统、数据库、中间件、应用程序来解决这个问题。

浅淡事务的隔离级别与锁

前面谈到了解决“海量事务快速处理”须要定制,那么接下来就须要考虑这个定制过程的核心问题是什么?要“海量快速”,就要能并行处理,并且是多台计算机并行处理,此时与事务的ACID属性关系最密切的就是隔离性。也就是说当多事务同一时候进行时,两个事务之间的影响情况,一般来说隔离性须要处理三个问题:脏读、不可反复读、幻读。

  • 脏读:一事务对数据进行了增删改,但未提交,有可能回滚,还有一事务却读取了未提交的数据
  • 不可反复读:一事务对数据进行了更新或删除操作,还有一事务两次查询的数据不一致
  • 幻读:一事务对数据进行了新增操作,还有一事务两次查询的数据不一致

以预订车票为例,脏读就是当一个预订车票的操作中,标记了某个座位已预定,可是没有为该身份证预订一张车票时,其它预订车票的操作读取该座位的状态时,不会立马获得该座位已预定状态,而是当第一个预订车票的操作完毕后,获得该座位已预定状态,或者当第一个预订车票的操作回滚后,获得该座位未预定的状态。读者能够參照不可反复读的解释构照对应的场景。

一般来说事务都须要禁止脏读和不可反复读。而对于幻读就须要依赖于实际的需求展开分析,以预订车票为例,当你查询剩余金额的时候,看到车票有剩余金额,可是进入预订的时候,发现没有剩余金额,假设把你的整个操作视同一个事务的话,这就是幻读,当然一般对于用户来讲是不禁止幻读的,可是在交易系统内部则经常要禁止幻读。

在现代的数据库系统中,一般能够为事务直接设置隔离级别,以期避免对应的问题。最高的隔离级别就是串行化,也就是相当于“一个一个运行”。在单机測试中,串行化的性能低至禁止不可反复读但同意幻读时的30分之中的一个,这也就是禁止幻读的代价。对于多台计算机而言“一个一个运行”相当于单机的效果,没能体现多台计算机的计算能力,是不可接受的,此时就须要採取其它的办法。眼下主要有两个策略来解决问题。

以预订车票为例,串行化就是指全部的预订车票操作一个一个处理,看上去这个方式非常笨拙,事实上在现实生活中的排队就是典型的串行化。在现实生活中,常常会有通过预分配资源的方式,来创建多个排队来提升速度,比如在火车票还是一个小硬纸片的时代,火车站的售票大厅中每一个窗体销售不同车次的火车票,这就是预分配资源,买票须要先依照车次选择窗体,然后排队购买。有过经历的读者立即就会发现,尽管预分配资源能够通过排多个队来提升效率,可是假设预分配的方案不合理,则会造成一个队排得非常长,而还有一个队没有人的情况。进一步的,预分配资源的方案无法避免一个人排了一个队之后,再排一遍,或者排还有一个队。

一种是通过建立“快照”的方式,在须要避免“幻读”时,就创建一个快照,此后这个事务中的全部读取的操作都是针对于这个快照进行操作,因此就不会有“幻读”发生,当然在数据库中实现快照的方式并非真的复制一遍数据库,而是通过记录数据的时间戳的方式来实现,对于单机来说,须要额外的存储和计算来记录并处理时间戳,对于多机来说,保持时间戳的同步也须要相当的代价,因此一般仅用于特定的场合。

通行的设置隔离级别的方式是加锁,锁的情况有非常多种,并且不同的数据库的产品中的实现方式也不尽同样,在此不展开解说,一般分成三种情况,数据库依照语句加锁,带有加锁命令的语句,在声明事务的同一时候声明隔离级别。须要注意是锁会导致死锁。此时应用中两个或多个事务发生永久堵塞(等待),每一个事务都在等待其它事务占用并堵塞了的资源。比如,假设事务A锁住了数据1并等待数据2,而事务B锁住了数据2并等待数据1,这样两个数据就发生了死锁现象。

以预订车票为例,当查询该身份证是否已预订过同样乘车日期同样车次的车票时,就相当于给这个身份证加上了锁,直到预订车票的操作成功或者失败才解锁,在此期间其它查询该身份证是否已预订过同样乘车日期同样车次的车票的操作都被堵塞(等待)。当查询该车次是否有空余座位时,也就相当于给这个车次加上了锁,直到预订车票的操作成功或者失败才解锁,在此期间,其它查询该车次是否有空余座位的操作都被堵塞。

当发生死锁后,就须要解除死锁状态,否则很多其它事务在请求处于死锁状态的数据时,就会发生连锁反应,解除死锁状态的主要方式是选择回滚当中的某一个事务。解除死锁毕竟是一种消极的处理方式,积级的处理方式应当是预防死锁、避免死锁,同一时候辅助以检測死锁避免出现连锁反应。在现代的数据库系统中,一般都提供各种加锁的方式以及比較智能的处理锁。对于支持多台计算机部署的场景中,也会把锁延伸至多台计算机中,也就是说,一台计算机上的数据库加锁的时候,同一时候会影响到其它计算机上的数据库,从而保证在多台计算机上配置成一套数据库系统时,执行在不同计算机上的事务的隔离级别和执行在单台计算机上的隔离级别表现一致。

第二部分

电子商务中事务的核心算法问题

在电子商务的场景中,由于涉及到財务(钱)的操作,就须要保证事务的隔离级别足够高不会导致出错。在实际需求中,不管是钱的剩余金额,还是待售商品的存量,都会涉及到在一个表中查询记录并求和这种操作。假设应用将一个操作直接请求到数据库时,为避免在并发操作时出现两次查询的数据不一致的情况,就须要隔离级别中禁止幻读。而隔离级别禁止幻读时在多台计算机上的的性能严重下降,难以满足要求。这种现象说明不适合借助于数据库本身的锁机制来实现电子商务中“海量事务快速处理”的请求(对于数据库本身支持一定的中间件特性的情况依照中间件来讨论)。

既然应用直接訪问数据库不适合“海量事务快速处理”的场景,就有必要在中间添加中间件,把事务的协调处理交给中间件处理,由于计算量大,这个中间件须要部署在多台计算机上,共同构成一个分布式事务中间件。眼下市场上有非常多支持分布式事务的中间件容器,比如EJB,能够通过简单的配置和编程就能够获得所须要的事务隔离级别。可是在电子商务的实际场景中,假设使用容器提供的分布式事务将会遭遇和在数据库中直接实现时同样的困境,假设不依赖于容器本身的分布式事务,而是依照需求自行管理锁的话,容器本身不仅没有起到积极的作用,并且由于引入了额外的处理过程而大大减少性能,因此眼下世界上上处理速度最高的海量事务处理系统中,基本上都是自行开发。

自行开发分布式事务中间件,能够依照实际需求最大程度的优化,自行开发也不意味着没有规律可循,眼下在“海量事务快速处理”中自行开发分布式事务大多基于Paxos算法。 http://zh.wikipedia.org/zh-cn/Paxos%E7%AE%97%E6%B3%95 Paxos算法的基本内容能够參考维基百科上的介绍。本文不反复理论知识,仅探讨一些在实践中可能涉及到的问题。对于没有接触过Paxos算法的朋友,首先要理解Paxos是一个怎么样的算法,Paxos算法并不像JPEG、PNG、MPEG等等算法,Paxos算法提供了一个为分布式系统怎样就某个值(决议)达成一致的模型。Paxos算法证明了一个符合Paxos模型的实现必定能保证分布式一致性,不须要针对于每次的详细实现再次证明,当然,假设有时间和精力的情况下,证明一次Paxos算法有助于加深对于该算法的理解。其次要知道Paxos算法是眼下解决分布式系统一致性算法中最有效的算法。

刚才提到Paxos算法解决的是分布式系统一致性,而实际场景中从分布式事务到分布式系统一致性还须要依照详细的情况拆分事务。这个拆分过程须要重复论证,不论什么一个环节的设计缺陷都将导致整个分布式系统一致性失效。假设您刚才已经看到了Paxos算法的简要介绍,您可能会注意到两个细节,在Paxos算法中值是保存在某一个分布式节点上,每一个节点中保存的值同意是不同的,因此对于获取值这种操作也是针对于某个节点的,放在实际的计算机环境中也就意味着,数据库被绑定在中间件上,其它的应用和中间件都从相关的中间件上获取数据,而不是訪问数据库。第二个细节就是提案依赖于编号,因此在实际的计算机环境中须要保证一个能满足海量事务的编号机制。

安全问题与优化思想

除了算法问题外,还须要特别关注安全问题,“海量事务快速处理”的安全问题主要是在系统负荷暴增的情况下,或者有若干台server宕机时,怎样保证整个系统仅仅是慢下来,而不是崩溃。一个针对类似问题的研究以前引起美国政界的轩然大波,一位来自中国的留学生发表了一篇《怎样对美国电网的缺陷进行梯级式攻击》的论文,其核心思想就是怎样用最少的代价引起一个局部系统故障,而局部系统的故障可能导致更大范围内的系统故障。在分布式系统中普遍存在这个问题,而对于基于事务的“海量事务快速处理”则更应重视这个问题。再举一个计算机的样例,Linux操作系统有一个Load值,这个值代表着一段时间内平均须要处理的进程数,保持计算机能及时处理进程的话,Load值应为总内核数的70%,而Load值超过总内核数的100%的话,计算机的性能不会获得提升,相反由于CPU须要花费很多其它的时间在进程调度上。

以预定车票为例,假设一个系统每秒种能处理一千次交易的话,那么一般来说,假设有700次交易随机分布在这一秒钟提交到系统的话,有一半以上的交易提交到系统的时候,系统正处于空暇状态,能够立马处理。假设有1000次交易随机分布在这一秒种提交到系统的话,那么全部的交易都要先等待之前的交易完毕后才干被系统处理。从系统的角度来说,不仅须要在处理交易的同一时候,堵塞兴许的交易,同一时候还须要在完毕一个交易后,选择一个没有和眼下正在处理的其它交易有冲突的交易,这样的推断自然要比系统空暇时不须要推断麻烦得多。假设负荷继续攀升的话,系统须要不断的堵塞兴许的交易,反而减慢了正在处理的交易。眼下12306.cn站点能够简单依照这样的思路来理解。

刚才提到了在“海量事务快速处理”系统中,数据库已经被融入了中间件中。而中间件的设计基于Paxos算法,与传统意义上基于中间件容器的设计不同,一些在中间件容器的模型中放在中间件的功能在“海量事务快速处理”系统中可能放在应用中实现,也有可能正好相反,一些在中间件容器的模型中放在应用中的功能在“海量事务快速处理”系统中可能放在中间件中实现。更进一步,操作系统也须要依照实际的应用和中间件调优。这意味着在“海量事务快速处理”系统中,从应用到中间件到数据存储到操作系统都是一体化考虑的,唯有如此,才干达到快速。

形象的说,“海量事务快速处理”系统最后往往分不出哪一块是应用,哪一块是数据库。换言之,假设能像通用系统一有明显的应用、数据库、操作系统、中间件的界限的话,事实上就失败了,由于没有优化到极致,用武侠小说的话讲以无招胜有招,通用系统就是“有招”,而“海量事务快速处理”系统必须做到“无招”,当然了,也能够称之为“屠龙之技”,由于国内应用场景非常少,在国外也主要是大型企业可能涉足这个领域。

不必重蹈覆辙

既然整个系统是一体化定制,就必定会导致牵一发动全身,在实际环境中,特别是互联网瞬息万变,需求不可能一成不变,这时在需求环节中就须要精准分析需求,同一时候在前期要考虑前面提到的算法问题和安全问题。由于在整个后面的过程中,核心需求是定死的,不可能有不论什么变化的,并且部署和运维也纳入到研发中的所有定制。这么多因素一起考虑,要定制中间件、操作系统和数据库,可想而知开发难度有多大了。

在实践中,特别是在没有遇到瓶颈的时候,一个通用系统的解决方式,比方说Linux + Oracle + J2EE + Tomcat 更easy获得认可。而在遭遇瓶颈后,由于整个系统的架构已经定型,全面提升为“海量事务快速处理”系统不仅有研发的投入,还有数据遗留资产的升级等等问题。最后往往採用牺牲局部的方式不停的打补丁来解决。这也是眼下国内非常少见到“海量事务快速处理”系统的原因。毫不夸张的说,国内多数发展迅猛的电子商务企业都正在面临这种困境,能够预见是否有决心定制“海量事务快速处理”系统将成为这些企业是否能胜出的决定因素。

EJB方案之所以不适合,是由于所用的Oracle + J2EE + Tomcat都是通用的工具,里面有大量的为了符合通用标准而具有的模块,这些模块尽管让这些工具能够适合在多种场景下应用,但在特定场景下却成了影响系统性能的垃圾,当这些“垃圾”须要清除的时候,眼下的软件公司基本上束手无措。

当然,不採用EJB方案不代表没有中间件,前面提到的Paxos算法,在实践中也是以中间件的形式体现,当然,在这样的场景中的定制开发的中间件已经和通用方案中借助于中间件容器实现的中间件已经大不同样,可是有一点要值得注意,全部的“写”操作,都是借助于中间件来完毕的,而不是借助于数据库中的事务操作。

从大量的经验教训来看,对于已经预见到“海量”的电子商务站点,不要採用EJB等通用中间件容器方案;而对于有着“海量”发展预期,可是眼下还没有达到“海量”的电子商务站点,核心业务的全部的“写”操作要借助于中间件而不是数据库。通常“海量事务快速处理系统”造价昂贵,应用范围不广,除了电子商务公司自行研发的系统外,眼下涉足该领域的研究机构也很少,清华大学web与软件技术研究中心是中国极少的研究此技术的权威机构,有宝贵的成功经验。

第三部分

“海量事务快速处理”的演进道路

前面讲了一些经验教训,在实践中非常少有像12306.cn这种能够预见到“海量”的电子商务站点,很多其它的有着“海量”发展预期,可是眼下还没有达到“海量”的电子商务站点,这些电子商务站点一方面受客观因素制约不可能一步到位设计“海量事务快速处理”系统,还有一方面发展期间同一时候也是业务探索期间,也不太可能明白哪一块业务将发展成为“海量”。此时就须要在实际建设之前规划一个演讲的道路,避免陷入误区。以下讲一下“海量事务快速处理”的演进道路。

首先要特别重视需求,既然核心系统将是所有定制开发,因此需求变更假设影响到了核心系统的话,那么代价将是很昂贵的。所以要尽可能的精确调研需求,避免模糊需求影响到核心系统,下图就是广为流传的因为需求理解的偏差,导致终于结果的严重偏差。

假设定性的话,以前有一个研究,一个缺陷假设在需求阶段修正要花1美元的话,那么在设计与撰写阶段要花2美元,假设是在完毕后修正的话,平均会花费69美元,例如以下图所看到的。这还是针对项目而言。因为互联网站点的升级必需要继承全部的数据遗留资产,因此互联网站点修正的代价将更加高昂,而对于处于核心的“海量事务快速处理”系统来说,严重缺陷的修正代价可能是不可想象的。

电子商务站点产品设计基础

既然需求对于电子商务站点是如此重要,那么该怎样精确的分析需求并避免错误呢,一般来说,能够将一个站点分成例如以下图所看到的的七种产品型态。

这七种产品形态能够用例如以下的经验方式界定。

对于主要的电子商务站点来说,主要包括CMS、MIS、OA、ERP这四种产品形态,这四种产品形态在电子商务站点中的常见体现形式例如以下图所看到的。

从图中能够看出,核心交易系统的需求是ERP中的一部分。以下继续分析ERP产品形态。一般来说ERP系统有三个层面——Data、Process、Action,大多数的ERP系统实现了Data和Process二个层面,Action眼下尚处于探索阶段。假设用一种简单的方式理解的话,Data就是与核心交易系统的MIS,而OA就是与核心交易系统的OA,例如以下图所看到的。

通过上面的介绍,读者能够看到,电子商务站点产品设计的核心之处在于怎样分析出核心交易系统的需求,换句话说,假设在产品设计阶段就不重视核心交易系统的话,那么在未来也将投入巨大的成本在修正缺陷上。

初期核心交易系统程序设计演示样例

前面提到,为了长期的发展,从一開始全部的核心交易的“写”操作都应当通过一个交易中间件,从架构模式上来看,属于Blackboard“黑板”模式。在实践中,一開始因为需求变化频繁,在没有特殊要求的场景中,“读”操作能够直接读取数据库,为了缓解压力,也能够借助于数据库的复制功能,下图就是一个核心交易系统中间件的部署示意图。

因为需求随着发展将不断变化,因此一開始非常难有清晰核心交易系统的边界,所以在程序设计上採用接口设计方式,设计接口须要遵循三个标准:

  • 依照不同来源划分接口
  • 接口都体现价值点。在本例中,用户的价值点除了拥有虚拟货币外,还拥有“票”(详细业务相关,不展开讨论),因此每个接口或者和价值点有关,或者和待售物品有关
  • 接口应当具有明白的业务意义

接口设计示比例如以下图所看到的。

交易系统的外部接口在处了了一些业务逻辑后,终于都将调用核心交易系统的处理过程,这个过程应当遵循例如以下的3个标准:

  • 初期能够採用“一个一个处理”的串行化方式将涉及到同一个价值点的多个处理过程排队,以保证ACID,比如在Java中能够直接使用Synchronizedkeyword
  • 每一个处理过程应尽量为很多其它的接口服务,也就是说尽量把外部接口转换成最少的核心交易处理过程
  • 每一个处理过程应涉及两个价值点,比如虚拟货币和物品,最多不超过3个

核心交易系统处理过程的序列图示比例如以下图所看到的。

在实际的生产环境中,该系统的日最高以前处理约10万笔交易。当然,这个数值和详细的项目需求密切相关,随着复杂程度的提高,处理能力可能会直线下降,既便如此,也能够满足起步电子商务站点的需求了。从公开的消息能够看到一些參考数据,在2011年11月11日的“双十一”促销期间,京东商城日订单量超过了40万单。

以预订车票为例,核心交易系统要处理的就是身份证、指定日期的指定车次这样一个交易。连钱都不须要处理,由于钱是在预订成功之后支付的。通过本例能够看出,传统的中间件容器方案中,为了避免负荷被中间件容器放大,一般都会仅把核心业务放在中间件容器中,而在自行研发交易中间件时,全部程序按需开发,同一时候为了应对未来必将出现的需求变更,将一部分需求在交易中间件中实现,也就是模糊了应用和中间件的界限。

发展期核心交易系统程序设计探讨

当电子商务站点继续发展,上面讲到的单机的核心交易系统已经无法满足需求时,就须要进一步提升核心交易系统的处理能力。一般有以下两种策略,一种是资源预分配、还有一种是离线锁。

前面提到了资源预分配,也讲到了资源预分配的缺点,可是资源预分配仍是一个很有效的提升核心交易系统处理能力的方法。以预订车票为例,既然火车的座位数都是能够预知的,那么全然能够预先为每一天的每个车次的每个座位准备好空位,就差填写身份证了。假设一台计算机无法处理全部的交易,能够依照每一天的每个车次来分配在不同的计算机上,这样针对于特定日期特定车次的交易就能够定位在特定的计算机上,而该计算机仅仅须要保证在本机上事务成功提交就可以,由于依照业务逻辑,这样的预分配资源的方式能够保证计算机之间不会冲突。

针对于春运期间火车票的特殊场境,资源预分配能够进一步优化,比方说对于当天有票释放出的车次,由于訪问量巨大,所以尽量拆分开分配分配到独立的计算机上处理,比如每种类型的车次一个计算机集群,对于昨天有票释放出的车次,在春运期间能够觉得基本上已经满了,能够把几种类型的车次合并为一个计算机集群,而对于更早释放出的车票,能够觉得仅仅剩下退票的情况了,能够把全部类型的车次合并为一个计算机集群,示意图例如以下所看到的,当然实际情况要复杂得多。

资源预分配策略能够在一定时期内有效缓解核心交易系统的压力。对于某些负荷变化大的应用,也能够採用资源预分配的策略,比如团购,每一件商品的数量是有限的,非常有可能被瞬间抢购一空,此时就应该预先为每一件商品分配空位。类似的,对于秒杀来说,商品数量非常少,系统瞬间负荷非常大,能够为每一件商品分配一台计算机处理。当然实际情况要复杂得多。

除了资源预分配之外,另一种处理策略是离线锁,在架构模式中,离线锁包括了乐观离线锁和悲观离线锁两种情况。离线锁设计的初衷是用来解决当client长期操作一个数据而锁定数据的时候,server端无法同一时候处理大量的在线锁的场景。在核心交易系统的场景中,一台响应用户请求的计算机替代了client的角色,每一个须要保证事务的数据(一般是一组数据)相应一个锁记录,在每次请求的过程中,先在锁记录上通过一个原子操作将状态从未锁定状态置为锁定状态,然后操作数据(一般是一组数据),在操作完毕后再在锁记录上通过一个原子操作将锁定状态置为未锁定状态。这就是典型悲观离线锁的使用方式。假设把锁记录独立为一个服务的话,就称为锁服务。

在实际场景中,通常是依照资源预分配策略来划分离线锁中锁定的数据粒度,比方说在电子商务站点中,一个用户所持有的全部价值点相应于一个锁,这些价值点包含他的账户、各种优惠券等等,某一个品种的普通商品相应于一个锁,当然也要考虑到前面提到的每一件商品相应于一个锁的情况。并且一般一个事务会锁定二个到三个数据,比如在一次消费过程中,先锁定用户,锁定商品,然后操作用户、操作商品,最后释放用户锁、释放商品锁。假设在操作过程中发现异常,则在释放锁之前恢复数据,保证一致性。

“海量事务快速处理”期核心交易系统分析探讨

当系统演讲到海量事务快速处理阶段,如前面所述,整个交易系统是全然依照需求定制的,以下仅就一些共性的问题做简要的探讨。主要涉及三方面的问题,系统的可用性,锁的粒度,以及事务的划分。

首先看系统的可用性,不论什么系统随着规模的增长,设备故障的频率也将越来越高,此时就须要有对应的容灾策略。须要消除单点、甚至于单集群故障。在前面提到的锁服务也就不能採用通用的数据库来设计。眼下流行的做法是採用基于Paxos算法的锁服务,通过五台计算机(最少是三台,通常是奇数个,考虑容灾的须要一般用五台,很多其它的计算机会造成之间通讯量的高速增长)组成一个针对某类资源的锁服务集群。在操作某一项资源时,先在锁服务上对这一项加锁,操作之后解锁。这和悲观离线锁机制同样,当然在实际操作中,由于还要消除保存资源数据的计算机的单点故障,加锁和解锁的过程要和全部数据的一致性保持同步。

既然锁是以锁服务的形式存在,那么怎样划分锁的粒度就成为效率的关键,锁的粒度太细的话,一个事务中操作锁的次数就太多了,锁的粒度太粗的话,碰撞的可能性就大得多。另外,既然锁已经独立于数据,因此一组锁服务能够为几类数据量不大的数据服务,对于数据量非常大的数据能够分成几组锁服务。

锁的粒度划分又和事务紧密联系在一起,一般来说,一个事务中持有的锁越多,碰撞的机会就越多,而碰撞的结果就是回滚。从定性的分析来看,能够用N^2来评价,比方说一个事务中持有两个锁,那么碰撞的机会就是4,持有三个锁的话,碰撞的机会就是9。也就是说,假设把一个须要持有三个锁的事务划分成两个持有两个锁的阶段的话,碰撞的机会就是2*4=8<9。从这个简单的定性分析能够看出,事务应以2个锁为主,不超过3个锁。4个锁以上的事务一定要分解成多个小事务。当然经过分解之后,复杂程度也随之提升,可是由于降低了碰撞,系统的整体性能获得了提升。也就在另外的一些环节中降低了系统的复杂度。

系统的可用性、锁的粒度以及事务的划分这三个问题既相互独立,又相互联系。对于核心交易系统来说,合理的事务划分能够提升性能,从而降低计算机的数量,间接提升了可用性。

另外一些误区

在前面提到了採用类似于EJB的中间件容器或者将事务交给数据库处理是两个基本的误区,以下谈一谈其它一些认识上的误区。

加强CDN建设能提升核心交易系统性能。答案是没有直接联系,对于电子商务站点来说,CDN的价植很多其它体如今CMS产品上,也就是货架陈列产品上,对于涉及到事务的MIS、OA、ERP来说,不可能使用CDN的缓存,最多使用CDN的路由加速功能。这些对于核心交易系统没有直接联系。

将核心交易系统建设成开放平台有助于简化核心交易系统,提升交易性能。答案正好相反。内部系统纵然有种种弊端,可是对于不论什么一个细节都是可控的,而对于开放平台来说,难以控制外部的操作,为了应外可能出现的负荷变化可能要做很多其它的准备。换句说话,在内部系统还没有完好的时候,开放平台不仅没有帮助,反而可能放大如今的缺陷引发连锁崩溃。

对于其它的误区欢迎各位联系,我将继续补充。

作者简单介绍

作者胡争辉,CSDN博客专家,CTO俱乐部成员。曾任完美时空(现更名为完美世界)顾问,承担互联网方面的部分管理工作。如今主要精力研究互联网产品设计,是Axure授权的高级咨询顾问和高级培训讲师。

个人博客:http://blog.csdn.net/hu_zhenghui

个人站点: http://bbs.hpx-party.org/

个人邮箱:hu@hpx-party.org huzhenghui@139.com

新浪微博:@胡争辉

QQ:443089607

GMail& GTalk: huzhengh@gmail.com

Fetion:293089651

声明

本文不是专业的学术论文,重点为介绍相关背景知识,写作过程有失严谨之处敬请同行斧正。假设各位认为有必要继续深入探讨,我将继续写点东西和各位探讨財务系统的分析设计。

版权全部,未经同意,禁止不论什么形式的转载、引用、摘要。首发地址: http://bbs.hpx-party.org/thread-8488-1-1.html 。假设须要请联系作者。

《今天你买到票了吗?——从铁道部12306.cn站点漫谈电子商务站点的“海量事务快速处理”系统》的更多相关文章

  1. 简单物联网:外网访问内网路由器下树莓派Flask服务器

    最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...

  2. 利用ssh反向代理以及autossh实现从外网连接内网服务器

    前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...

  3. 外网访问内网Docker容器

    外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...

  4. 外网访问内网SpringBoot

    外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...

  5. 外网访问内网Elasticsearch WEB

    外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...

  6. 怎样从外网访问内网Rails

    外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...

  7. 怎样从外网访问内网Memcached数据库

    外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...

  8. 怎样从外网访问内网CouchDB数据库

    外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...

  9. 怎样从外网访问内网DB2数据库

    外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...

  10. 怎样从外网访问内网OpenLDAP数据库

    外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...

随机推荐

  1. scrollwidth ,clientwidth ,offsetwidth 三者的区别

    clientwidth:内容可视区域的宽度 offsetwidth:元素整体宽度 scrollwidth:实际内容的宽度

  2. Lusac定理

    转载大佬的模版:http://www.cnblogs.com/vongang/archive/2012/12/02/2798138.html

  3. ln用法

    第一部分: 建立简单的硬连接: ln ./wwy.gif ./wwy_ln (第二个参数为新建的连接文件,建立前不存在),则任意一个文件变化,另一个也变化:大小为一个文件的大小:硬连接只能建在同一个分 ...

  4. mac自己定义tree命令

    编辑文件: vim ~/.bash_profile 在文件末尾追加: alias tree="find . -print | sed -e 's;[^/]*/;|____;g;s;____| ...

  5. Django加入JS,CSS,图片等外部文件的方法

    Django加入JS,CSS.图片等外部文件的方法 By 白熊花田(http://blog.csdn.net/whiterbear) 转载需注明出处,谢谢. 在使用Django搭建站点时,往往须要使用 ...

  6. libev环境

    wget https://download.libsodium.org/libsodium/releases/libsodium-1.0.13.tar.gz tar xzvf libsodium-1. ...

  7. 求第k大的数(用到快速排序算法的思想)

    //下面两种part效率比较:相同运算量下part比part2快5倍左右,part2写法简单但是效率低 #include "stdafx.h" #include <iostr ...

  8. 银行测试 http://blog.csdn.net/stillming/article/details/42275251

    从一家工作了五年的软件公司的测试管理者跳槽到**银行做软件测试,短短两个月,对银行测试有了初步认识,总结和记录下来,加深个人的理解,同时也共享给各位. 银行作为大家的理财顾问,对金钱非常敏感,频繁甚至 ...

  9. 小米开源文件管理器MiCodeFileExplorer-源码研究(0)-初步研究

    2011年对着书本Android应用开发揭秘,写了2个月的HelloWorld.   现在想复习并深入,我没有耐心再去一点点地敲代码了.   4年前自己是个学生,实习,现在有工作,只能业余时间研究. ...

  10. WCF 字节数据传输

    准备工作 1.新建一个工程,添加一个WCF服务库, 然后公共的类库, 添加一个默认可序列化的的CompositeType类用于压缩. [Serializable] public class Compo ...