改成
struct airline
{
char air_num[10];/航班号/
char flycity[20];/出发城市/
char getcity[20];/到达城市/
char flytime[10];/起飞时间/
char gettime[10];/到达时间/
int price;/价格/
int discount;/折扣率/
int tkt_amt;/总票数/
int tkt_sur;/剩余票数/
linklist order;/乘员名单域,指向乘员名单链表的头指针/
linkqueue wait;/等候替补的客户名单域,分别指向排队等候名单队头队尾的指针/
}lineinfo;
struct airline start;
struct airline air[MAXSIZE];
void input()/录入航班情况/
{
int i;
char air_num[10];/航班号/
char flycity[20];/出发城市/
char getcity[20];/到达城市/
char flytime[10];/起飞时间/
char gettime[10];/到达时间/
int price;/价格/
int discount;/折扣率/
int tkt_amt;/总票数/
int tkt_sur;/剩余票数/
printf("请输入航班信息:\n",_flushall());
for(i=0;i<=MAXSIZE;i++)
{
printf("航班%d:",i+1,_flushall());
printf("\n 航班号:",_flushall());
scanf("%s",&air[i]air_num);
printf("出发城市:");
scanf("%s",&air[i]flycity);
printf("到达城市:");
scanf("%s",&air[i]getcity);
printf("起飞时间:");
scanf("%s",&air[i]flytime);
printf("到达时间:");
scanf("%s",&air[i]gettime);
printf(" 价格:");
scanf("%d",&air[i]price);
printf(" 折扣率:");
scanf("%d",&air[i]discount);
printf(" 总票数:");
scanf("%d",&air[i]tkt_amt);
printf("剩余票数:");
scanf("%d",&air[i]tkt_sur);
}
}
问题一:常用的数据库软件有哪些? SQL Server 是 Microsoft(微软) 的数据产品,它的易用性强! Oracle 是 Oracle(甲骨文)公司的数据产品!号称世界上最好的数据系统! DB2 是IBM公司的产品,在全球500强的企业中有80%是用DB2作为数据库平台的
问题二:常用数据库有哪些?他们有什么区别 开源的Mysql顶;PostgreSQL即开放源码的
商业的Oracle/SQL Server/DB2即收费的
问题三:什么是常用的三个数据库? 目前,数据库管理系统关系型数据库为主导产品的商品化,技术相对成熟。虽然面向对象的数据库管理系统的先进技术,数据库易于开发,维护,但尚未成熟的产品。国际和国内领先的关系数据库管理系统,甲骨文,Sybase,Informix和INGRES。这些产品支持多种平台,如UNIX,VMS,Windows上,而不是同一级别的支持。和成熟的IBM的DB2关系数据库。但是,DB2是内嵌于IBM的AS/400系列机,只支持OS/400 *** 作系统。
?1MySQL
?MySQL是最受欢迎的开源SQL数据库管理系统,由MySQL AB公司,发布和支持。 MySQL AB是基于MySQL开发一个商业公司,它是利用与开源值相结合的一个成功的商业模式?和方法论的第二代开源公司。 MySQL是MySQL AB的注册商标。
?MySQL是一个快速,多线程,多用户和健壮的SQL数据库服务器。 MySQL服务器支持关键任务,重负载生产系统的使用,它可以嵌入到一个大配置(大规模部署)软件。
?的MySQL与其他数据库管理系统相比,具有以下优点:
?(1)MySQL是一个关系数据库管理系统。
?(2)MySQL是开源。
?(3)MySQL服务器是一个快速,可靠和易于使用的数据库服务器。
?(4)在MySQL服务器的客户机/服务器或嵌入式系统。
?(5)可以使用MySQL软件。
2SQL Server的吗?
?SQL Server是由微软开发的数据库管理系统,是目前最流行的数据库,用于存储在网络上的数据,它已被广泛用于电子商务,银行,保险,电力和其他数据库相关的产业。
?SQL Server 2005的最新版本,它只能在Windows作业系统的稳定运行是非常重要的数据库。并行实施和共存模型并不成熟,这是很难对付越来越多的用户和数据量是有限的,可扩展性。
?SQL Server提供了网络和电子商务功能,如丰富的XML和Internet标准的支持,轻松且安全地通过Web访问的数据的范围很广,有一个强大,灵活和网络,基于安全和应用管理。此外,由于它的易用性和友好的用户界面,通过广大用户的好评,。
?3Oracle
?提出的数据库,该公司首先想到的,通常是甲骨文(Oracle)。该公司成立于1977年,原是一个专门开发的数据库公司。甲骨文一直在数据库领域的领导者。 1984年,第一个关系数据库转移到一台台式电脑。然后,Oracle5率先推出的分布式数据库,客户机/服务器体系结构的新概念。甲骨文公司的第一行锁定模式和对称多处理计算机的支持最新的Oracle对象技术,成为关系 - 对象数据库系统。目前,甲骨文的产品涵盖了几十个型号的大,中,小型机,Oracle数据库已成为世界上使用最广泛的关系数据。
Oracle数据库产品具有以下优良特性。
?(一)兼容性
?Oracle产品使用标准的SQL,和美国国家标准技术局(NIST)测试后。兼容IBM的SQL / DS,DB2中,安格尔的IDMS / R。
?(2)可移植性
??甲骨文的产品,可以广泛的硬件和 *** 作系统平台上运行。可以安装在超过70种大不同,VMS系统的DOS,UNIX上,Windows和其他 *** 作系统,小型机;
?(3)协会
甲骨文与各种通信网络连接,支持各种协议(TCP / IP协议说,DECnet,LU62工作等)。?
?(4)高生产率
?Oracle提供了多种开发>>
问题四:现在一般常用的数据库有哪几种啊 ? MSSQL和MySQL两种 sqlserver即是常说的MSSQL, 是微软公司的产品,而MySQL不是,MySQL是一家叫MySQL AB的公司的开源产品,它是免费的,而且可以跨平台使用 MSSQL比较昂贵,一般只在Windows下使用
问题五:常用大型数据库有哪些 FOXBASE
MYSQL
这俩可算不上大型数据库管理系统
PB 是数据库应用程序开发用的ide,根本就不是数据库管理系统
Foxbase是dos时代的产品了,进入windows时代改叫foxpro,属于桌面单机级别的小型数据库系统,mysql是个中轻量级的,但是开源,大量使用于小型网站,真正重量级的是Oracle和DB2,银行之类的关键行业用的多是这两个,微软的MS SQLServer相对DB2和Oracle规模小一些,多见于中小型企业单位使用,Sybase可以说是日薄西山,不行了
问题六:常见的数据库文件格式有哪些? dbf dbase文件,一种由ashton-tate创建的格式,可以被act!、lipper、foxpro、arago、wordtech、xbase和类似数据库或与数据库有关产品识别,可用数据文件(能被excel 97打开),oracle 81x表格空间文件;dba是access文件;nsf lotus notes数据库;mdf和ldf是sql server文件;2另外你还有不少是软件开发者自己定义的数据库文件,大多采用dat,或者把dba转换为dat,由程序文件名转换处理。3odb++是一种可扩展的ascii格式,它可在单个数据库中保存pcb制造和装配所必需的全部工程数据。是能把多种数据格式数据库连接起来的桥梁,是一种双向格式,允许数据上行和下传。
问题七:常见的数据库应用系统有哪些? 现在极大多的企业级软件都是基于数据库的。
比如:
ERP: 企业资源管理计划
CRM: 客户关系管理
OA: 办公自动化。
12306铁道部的网上订票系统。
。。。
问题八:常用的数据库软件有哪些 1)MYSQL:
MySQL是由MySQL AB开发、发布和支持,是一个快速的、多线程、多用户和健壮的SQL数据库服务器,是目前最受欢迎的开源SQL数据库管理系统。MySQL
AB是一家基于MySQL开发人员的商业公司,它是一家使用了一种成功的商业模式来结合开源价值和方法论的第二代开源公司。MySQL是MySQL
AB的注册商标。MySQLMySQL服务器支持关键任务、重负载生产系统的使用,也可以将它嵌入到一个大配置(mass-
deployed)的软件中去。
2)ACCESS:
ACCESS是系统自带的一种关系式数据库,与空间共存不需要单独开设。关系式数据库由一系列表组成,表又由一系列行和列组成,每一行是一个记录,每一列是一个字段,每个字段有一个字段名,字段名在一个表中不能重复。Access数据库以文件形式保存,文件的扩展名是MDB,作为开发网站常用的数据库,Access数据库一般搭配ASP程序使用。当数据库过大的时候可以转换为MSSQL数据库使用,二者可以进行转换。
3)MSSQL:
SQL Server也是微软公司出品的,它是一个数据库平台,提供数据库的从服务器到终端的完整的解决方案,其中数据库服务器部分,是一个数据库管理系统,用于建立、使用和维护数据库。
MSSQL特点:数据承载量比较大,存储数据速度快,稳定性强,适用于中小型企业开发网站和办公系统常用的数据库。
4)Oracle:
Oracle公司是全球最大的信息管理软件及服务供应商,成立于1977年,总部位于美国加州 Redwood
shore。Oracle的软件可运行在PC、工作站、小型机、主机、大规模的并行计算机,以及PDA等各种计算设备上,随着越来越多的企业将自己转向电子商务,Oracle的具有强大的电子商务能力的解决方案,为企业提供高效率的扩展市场的手段,并提高工作效率和吸引更多的客户。Oracle提供的完整的电子商务产品和服务包括:用于建立和交付基于Web的Internet平台;
综合、全面的具有Internet能力的商业应用;强大的专业服务,帮助用户实施电子商务战略,以及设计、定制和实施各种电子商务解决方案。
问题九:常见的数据库管理系统有哪些?它们各自有什么特点? 1 IBM 的DB2作为关系数据库领域的开拓者和领航人,IBM在1977年完成了System R系统的原型,1980年开始提供集成的数据库服务器―― System/38,随后是SQL/DSforVSE和VM,其初始版本与SystemR研究原型密切相关。DB2 forMVSV1 在1983年推出。该版本的目标是提供这一新方案所承诺的简单性,数据不相关性和用户生产率。1988年DB2 for MVS 提供了强大的在线事务处理(OLTP)支持,1989 年和1993 年分别以远程工作单元和分布式工作单元实现了分布式数据库支持。最近推出的DB2 Universal Database 61则是通用数据库的典范,是第一个具备网上功能的多媒体关系数据库管理系统,支持包括Linux在内的一系列平台。2 OracleOracle 前身叫SDL,由Larry Ellison 和另两个编程人员在1977创办,他们开发了自己的拳头产品,在市场上大量销售,1979 年,Oracle公司引入了第一个商用SQL 关系数据库管理系统。Oracle公司是最早开发关系数据库的厂商之一,其产品支持最广泛的 *** 作系统平台。目前Oracle关系数据库产品的市场占有率名列前茅。3 InformixInformix在1980年成立,目的是为Unix等开放 *** 作系统提供专业的关系型数据库产品。公司的名称Informix便是取自Information 和Unix的结合。Informix第一个真正支持SQL语言的关系数据库产品是Informix SE(StandardEngine)。InformixSE是在当时的微机Unix环境下主要的数据库产品。它也是第一个被移植到Linux上的商业数据库产品。4 SybaseSybase公司成立于1984年,公司名称“Sybase”取自“system”和“database” 相结合的含义。Sybase公司的创始人之一Bob Epstein 是Ingres 大学版(与System/R同时期的关系数据库模型产品)的主要设计人员。公司的第一个关系数据库产品是1987年5月推出的Sybase SQLServer10。Sybase首先提出Client/Server 数据库体系结构的思想,并率先在Sybase SQLServer 中实现。5 SQL Server1987 年,微软和IBM合作开发完成OS/2,IBM 在其销售的OS/2 ExtendedEdition 系统中绑定了OS/2Database Manager,而微软产品线中尚缺少数据库产品。为此,微软将目光投向Sybase,同Sybase 签订了合作协议,使用Sybase的技术开发基于OS/2平台的关系型数据库。1989年,微软发布了SQL Server 10 版。6 PostgreSQLPostgreSQL 是一种特性非常齐全的自由软件的对象――关系性数据库管理系统(ORDBMS),它的很多特性是当今许多商业数据库的前身。PostgreSQL最早开始于BSD的Ingres项目。PostgreSQL 的特性覆盖了SQL-2/SQL-92和SQL-3。首先,它包括了可以说是目前世界上最丰富的数据类型的支持;其次,目前PostgreSQL 是唯一支持事务、子查询、多版本并行控制系统、数据完整性检查等特性的唯一的一种自由软件的数据库管理系统7mySQLmySQL是一个小型关系型>>
问题十:常用的数据库安全技术有哪些 数据库的安全性是指保护数据库以防止不合法的使用所造成的数据泄露、更改或破坏。
安全性问题不是数据库系统所独有的,所有计算机系统都有这个问题。只是在数据库系统中大量数据集中存放,而且为许多最终用户直接共享,从而使安全性问题更为突出。 系统安全保护措施是否有效是数据库系统的主要指标之一。 数据库的安全性和计算机系统的安全性,包括 *** 作系统、网络系统的安全性是紧密联系、相互支持的。
实现数据库安全性控制的常用方法和技术有:
(1)用户标识和鉴别:该方法由系统提供一定的方式让用户标识自己咱勺名字或身份。每次用户要求进入系统时,由系统进行核对,通过鉴定后才提供系统的使用权。
(2)存取控制:通过用户权限定义和合法权检查确保只有合法权限的用户访问数据库,所有未被授权的人员无法存取数据。例如C2级中的自主存取控制(I)AC),Bl级中的强制存取控制(M.AC)。
(3)视图机制:为不同的用户定义视图,通过视图机制把要保密的数据对无权存取的用户隐藏起来,从而自动地对数据提供一定程度的安全保护。
(4)审计:建立审计日志,把用户对数据库的所有 *** 作自动记录下来放人审计日志中,DBA可以利用审计跟踪的信息,重现导致数据库现有状况的一系列事件,找出非法存取数据的人、时间和内容等。
(5)数据加密:对存储和传输的数据进行加密处理,从而使得不知道解密算法的人无法获知数据的内容。
本文的重点不是在如何解决高并发的问题,而是希望从业务角度去分析,12306 的理想模型应该是怎么样的。网上目前谈 12306 的文章貌似都是千篇一律的只谈技术,不谈业务分析和如何建模的。所以我想写一下自己的设计和大家交流学习。
1、需求概述
12306 这个系统,核心要解决的问题是网上售票。涉及到 2 个角色使用该系统:用户、铁道部。用户的核心诉求是查询余票、购票;铁道部的核心诉求是售票。购票和售票其实是一个场景,对用户来说是购票,对铁道部来说是售票。因此,我们要设计一个在线的网站系统,解决用户的查询余票、购票,以及铁道部的售票这 3 个核心诉求。看起来,这 3 个场景都是围绕火车票展开的。
查询余票:用户输入出发地、目的地、出发日三个条件,查询可能存在的车次,用户可以看到每个车次经过的站点名称,以及每种座位的余票数量。
购票:购票分为订票和付款两个阶段,本文重点分析订票的模型设计和实现思路。
其实还有很多其他的需求,比如给不同的车次设定销售座位数配额,以及不同的区段设置不同的限额。但相比前面两个需求来说,我觉得这个需求相对次要一些。
2、需求分析
确实,12306 也是一个电商系统,而且看起来商品就是票了。因为如果把一张票看成是一个商品,那购票就类似于购买商品,然后每张票都有库存,商品也有库存的概念。但是如果我们仔细想想,会发现 12306 要复杂很多,因为我们无法预先确定好所有的票,如果非要确定,那只能通过穷举法了。
我们以北京西到深圳北的 G71 车次高铁为例(这里只考虑南下的方向,不考虑深圳北到北京西的,那是另外一个车次,叫 G72),它有 17 个站(北京西是 01号站,深圳北是 17号站),3 种座位(商务、一等、二等)。表面看起来,这不就是 3 个商品吗?G71 商务座、G71 一等座、G71 二等座。大部分轻易喷 12306 的技术人员(包括某些中等规模公司的专家、CTO)就是在这里栽第一个跟头的。实际上,G71 有 1363=408 种商品(408 个 SKU),怎么算来的?如下:
如果卖北京西始发的,有 16 种卖法(因为后面有 16 个站),北京西到:保定、石家庄、郑州、武汉、长沙、广州、虎门、深圳。。。。都是一个独立的商品,同理,石家庄上车的,有 15 种下车的可能,以此类推,单以上下车的站来计算,有 136 种票:16+15+14+2+1=136。每种票都有 3 种座位,一共是 408 个商品。
为了方便后面的讨论,我们先明确一下票是什么?
一张票的核心信息包括:出发时间、出发地、目的地、车次、座位号。持有票的人就拥有了一个凭证,该凭证表示持有它的人可以坐某个车次的某个座位号,从某地到某地。所以,一张票,对用户来说是一个凭证,对铁道部来说是一个承诺;那对系统来说是什么呢?不知道。这就是我们要分析业务,领域建模的原因,我们再继续思考吧。
明白了票的核心信息后,我们再看看 G71 这个车次的高铁,可以卖多少张票?
讨论前先说明一下,一辆火车的物理座位数(站票也可以看成是一种座位,因为站票也有数量配额)不等于可用的最大配合。所有的物理座位不可能都通过 12306 网站来销售,而是只会销售一部分,比如 40%。其余的还是会通过线下的方式销售。不仅如此,可能有些站点上车的人会比较多,有些比较少,所以我们还会给不同的区间配置不同的限额。
比如 D31 北京南至上海共有 765 张,北京南有 260 张,杨柳青有 80 张,泰安有 76 张。如果杨柳青的 80 张票售完就会显示无票,就算其他站有票也会显示无票的。每个车次肯定会有各种座位的配额和限额的配置的,这种配置我目前无法预料,但我已经把这些规则都封装近车次聚合根里了,所有的配置策略都是基于座位类型、站点、区间配置的。关于票的配置抽象出来,我觉得主要有 3 种:
某个区段最多允许出多少张;
某个区段最少允许出多少张;
某个站点上车的最多多少张。
当用户订票时,把用户指定的区段和这 3 种配置条件进行比较,3 个条件都满足,则可以出票。不满足,则认为无票了。下面举个例子:
ABCDEFG,这是所有站点。座位总配额是 100,假设 B 站点上车,E 站下车的人比较少,那我们就可以设定 BE 这个区段最多只能出 10 张票。所以,只要是用户的订票是在这个区段内的,就最多出 10 张。再比如,一列车次,总共 100 个座位配额,希望全程票最少满足 80 张,那我们只要给 AG 这个区段设定最少 80 张。那任何订票请求,如果是子区间的,就不能超过 100-80,即 20 张。这两种条件必须同时满足,才允许出票。
但是,不管如何做配额和限额,我们总是针对某个车次进行配置,这些配置只是车次内部售票时的一些额外的判断条件(业务规则),不影响车次模型的核心地位和对外暴露的功能。所以,为了本文讨论的清楚起见,我后续的讨论都不涉及配额和限额的问题,而是认为任何区段都可以享受火车最大的物理座位数。
并且,为了讨论问题方便,我们减少一些站点来讨论。假设某个车次有 A,B,C,D 四个站点。那 001 这个人购买了 A,B 这个区间,系统会分配给 001 一个座位 x;但是因为 001 坐到 B 站点后会下车,所以相当于 x 这个座位又空出来了,也就是说,从 B 站点开始,系统又可以认为 x 这个座位是可用的。所以,我们得出结论:同一个座位,其实可以同时出售 AB,BC 这两张票。通过这个简单的分析,我们知道,一列火车虽然只有有限的座位数,比如 1000 个座位。但可以卖出的票远远不止 1000 个。
还是以 A,B,C,D 四个站点为例,假如火车总共有 1000 个座位,那 AB 可以卖 1000 张,BC 也可以卖 1000 张,同样,CD 也可以卖 1000 张。也就是说,理论上最多可以卖出 3000 张票。但是如果换一种卖法,所有人都是买 ABCD 的票,也就是说所有的票都是经过所有站点的,那就是最多只能卖出 1000 张票了。而实际的场景,一定是介于 1000 到 3000 之间。然后实际的 G71 这个车次,有 17 个站,那到底可以卖出多少个票,大家应该可以算了吧。理论上这 17 个站中的任意两个站点之间所形成的线段,都可以出售为一张票。我数学不好,算不太清楚,麻烦有数学好的人帮我算算,呵呵。
通过上面的分析,我们知道一张票的本质是某个车次的某一段区间(一条线段),这个区间包含了若干个站点。然后我们还发现,只要区间不重叠,那座位就不会发生竞争,可以被回收利用,也就是说,可以同时预先出售。
另外,经过更深入的分析,我们还发现区间有 4 种关系:
不重叠;
部分重叠;
完全重叠;
覆盖。
不重叠的情况我们已经讨论过了,而覆盖也是重叠的一种。所以我们发现如果重叠,比如有两个区间发生重叠,那重叠部分的区间(可能夸一个或多个站点)是在争抢座位的。因为假设一列火车有 100 个座位,那每个原子区间(两个相邻站点的连线),最多允许重叠 99 次。
所以,经过上面的分析,我们知道了一个车次能够出售一张车票的核心业务规则是什么?就是:这张车票所包含的每个原子区间的重叠次数加 1 都不能超过车次的总座位数,实际上重叠次数 +1 也可以理解为线段的厚度。
3、模型设计
上面我分析了一下票的本质是什么。那接下来我们再来看看怎么设计模型,来快速实现购票的需求,重点是怎么设计商品聚合以及减库存的逻辑。
传统电商的思路
如果按照普通电商的思路,把票(站点区间)设计为商品(聚合根),然后为票设计库存数量。我个人觉得是很糟糕的。因为一方面这种聚合根非常多(上面的 G71 就有 408 个);另一方面,即便枚举出来了,一次购票也一定会影响非常多其他聚合根的库存数量(只要被部分或全部重叠的区间都受影响)。这样的一次订单处理的复杂度是难以评估的。而且这么多聚合根的更新要在一个事务里,这不是为难数据库吗?而且,这种设计必然带来大量的事务的并发冲突,很可能导致数据库死锁。
总之,我认为这种是典型的由于领域模型的设计错误,导致并发冲突高、数据持久化落地困难。或者如果要解决并发问题,只能排队单线程处理,但是仍然解决不了要在一个事务里修改大量聚合根的尴尬局面。
听说 12306 是采用了 Pivotal Gemfire 这种高大上的内存数据库,我对这个不太了解。我不可想象要是不使用内存数据库,他们要怎么实现车次内的票之间的数据强一致性(就是保证所有出售的票都是符合上面讨论的业务规则的)?所以,这种设计,我个人认为是思维定势了,把火车票看成是普通电商的商品来看待。所以,我们有时做设计又要依赖于经验,又要不能被以往经验所束缚,真的不容易,关键还是要根据具体的业务场景多多深入分析,尽量分析抽象出问题的本质出来,这样才能对症下药。那是否有其他的设计思路呢?
我的思路
1、聚合设计
通过上面的分析我们知道,其实任何一次购票都是针对某个车次的,我认为车次是负责处理订票的聚合根。我们看看一个车次包含了哪些信息?一个车次包括了:
车次名称,如 G71;
座位数,实际座位数会分类型,比如商务座 20 个,一等座 200 个;二等座 500 个;我们这里为了简化问题,可以暂时忽略类型,我认为这个类型不影响核心的模型的设计决策。需要格外注意的是:这里的座位数不要理解为真实的物理座位数,很有可能比真实的座位数要少。因为我们不可能把一个车次的所有座位都在网上通过 12306 来出售,而是只出售一部分,具体出售多少,要由工作人员人工指定。
经过的站点信息(包括站点的 ID、站点名称等),注意:车次还会记录这些站点之间的顺序关系;
出发时间;看过 GRASP 九大模式中的信息专家模式的同学应该知道,将职责分配给拥有执行该职责所需信息的类。
我们这个场景,车次具有一次出票的所有信息,所以我们应该把出票的职责交给车次。另外学过 DDD 的同学应该知道,聚合设计有一个原则,就是:聚合内强一致性,聚合之间最终一致性。经过上面的分析,我们知道要产生一张票,其实要影响很多和这个票对应的线段相交的其他票的可用数量。因为所有的站点信息都在车次聚合内部,所以车次聚合内部自然可以维护所有的原子区间,以及每个原子区间的可用票数(相当于是库存数)。当一个原子区间的可用票数为 0 的时候,意味着火车针对这个区间的票已经卖完了。所以,我们完全可以让车次这个聚合根来保证出票时对所有原子区间的可用票数的更新的强一致性。对于车次聚合根来说,这很简单,因为只是几次简单的内存 *** 作而已,耗时可以忽略。一列火车假如有 ABCD 四个站点,那原子区间就是 3 个。对于 G71,则是 16 个。
2、怎么判断是否能出票?
基于上面的聚合设计,出票时扣减库存的逻辑是:
根据订单信息,拿到出发地和目的地,然后获取这段区间里的所有的原子区间。然后尝试将每个原子区间的可用票数减 1,如果所有的原子区间都够减,则购票成功;否则购票失败,提示用户该票已经卖完了。是不是很简单呢?知道了出票的逻辑,那退票的逻辑也就很简单了,就是把这个票的所有原子区间的可用票数加 1 就 OK 了。如果我们从线段的厚度的角度去考虑,那出票时,每个原子区间的厚度就是 +1,退票时就是减一。就是相反的 *** 作,但本质是一样的。
所以,通过这样的思路,我们将一次订票的处理控制在了一个聚合根里,用聚合根内的强一致性的特性保证了订票处理的强一致性,同时也保证了性能,免去了并发冲突的可能性。传统电商那种把票单做类似商品的核心聚合根的设计,我当时第一眼看到就觉得不妥。因为这违背了 DDD 强调的强一致性应该由聚合根来保证、聚合根之间的最终一致性通过 Saga 来保证的原则。
还有一个很重要的概念我想说一下我的看法,就是座位和区间的关系。因为有些朋友和我讲,考虑座位号的问题,虽然都能减 1,座位号也必须是同一个。我觉得座位是全局共享的,和区段无关(也许我的理解完全有误,请大家指正)。座位是一个物理概念,一个用户成功购买了一张票后,座位就会少一个,一张票唯一对应一个座位,但是一个座位有可能会对应多张票;而区间是一个逻辑上的概念,区间的作用有两个:1)表示票的出发地和目的地;2)记录票的可用数额。如果区间能连通(即该区间内的每个原子区间的可用数额都大于 0),则表示允许拥有一个座位。所以,我觉得座位和票(区间)是两个维度的概念。
3、如何为票分配座位?
我觉得车次聚合根内部应该维护所有该车次已经售出的票,已经出售的票的的本质是区间和座位的对应关系。系统处理订票时,用户提交过来的是一段区间。所以,系统应该做两个事情:
先根据区间去判断是否有可用的座位;
如果有可用座位,则再通过算法去选择一个可用的座位;
当得到一个可用座位后,就可以生成一张票了,然后保存这个票到车次聚合根内部即可。下面举个例子:
假设现在的情况是座位有 3 个,站点有 4 个:
座位:1,2,3
站点:abcd
票的卖法 1:
票 1:ab,1
票 2:bc,2
票 3:cd,3
票 4:ac,3
票 5:bd,1
这种选座位的方式应该比较高效,因为总是优先从座位池里去拿座位,只有在万不得已的时候才会去回收可重复利用的票。
上面的 4,5 两个票,就是考虑回收利用的结果。
票的卖法 2:
票 1:ab,1
票 2:bc,1
票 3:cd,1
票 4:ac,2
票 5:bd,3
这种选座位的方式应该相对低效,因为总是优先会去扫描是否有可回收的座位,而扫描相对直接从座位池里去拿票总是成本相对要高的。
上面的 2,3 两个票,就是考虑回收利用的结果。
但是,优先从座位池里拿票的算法有缺陷,就是会出现虽然第一步判断认为有可用的座位,但是这个座位可能不是全程都是同一个座位。举例:
假设现在的情况是座位有 3 个,站点有 4 个:
座位:1,2,3
站点:abcd
票的卖法 3:
票 1:ab,1
票 2:bc,2
票 3:cd,3
现在如果有人要买 ad 的票,那可用的座位有 2,或者 3。但是无论是 2 还是 3,都要这个乘客中途换车位。比如卖给他座位 2,那他 ab 是坐的座位 2,但是 bc 的时候要坐座位 1 的。否则拿票 2 的那个人上车时,发现座位 2 已经有人了。而通过优先回收利用的算法,是没这个问题的。
所以,从上面的分析我们也知道选座位的算法该怎么写了,就是采用优先回收利用座位的算法。我认为不管我们这里怎么设计算法,都不影响大局,因为这一切都只发生在车次聚合根内部,这就是预先设计好聚合根,明确出票职责在哪个对象上的好处。
4、模型分析总结
我认为票不是核心聚合根,票只是一次出票的结果,一个凭证而已。
12306 真正的核心聚合根应该是车次,车次具有出票的职责,一次出票具体做的事情有:
判断是否可出票;
选择可用的座位;
更新一次出票时所有原子区间的可用票数,用于判断下次是否能出票;
维护所有已售出的票,用于为选择可用座位提供依据。
通过这样的模型设计,我们可以确保一次出票处理只会在一个车次聚合根内进行。这样的好处是:
不需要依赖数据库事务就能实现数据修改的强一致性,因为所有修改只在一个聚合根内发生;
在保证数据强一致性的同时还能提供很高的并发处理能力,具体设计见下面的架构设计。
4、架构设计
我觉得 12306 这样的业务场景,非常适合使用 CQRS 架构;因为首先它是一个查多写少、但是写的业务逻辑非常复杂的系统。所以,非常适合做架构层面的读写分离,即采用 CQRS 架构。而且应该使用数据存储也分离的 CQRS。这样 CQ 两端才可以完全不需要顾及对方的问题,各自优化自己的问题即可。我们可以在 C 端使用 DDD 领域模型的思路,用良好设计的领域模型实现复杂的业务规则和业务逻辑。而 Q 端则使用分布式缓存方案,实现可伸缩的查询能力。
1、订票的实现思路
同时借助像 ENode 这样的框架,我们可以实现 in-memory + Event Sourcing 的架构。Event Sourcing 技术,可以让领域模型的所有状态修改的持久化统一起来,本来要用 ORM 的方式保存聚合根最新状态的,现在只需要简单的通用的方式保存一个事件即可(一次订票只涉及一个车次聚合根的修改,修改只产生一个事件,只需要持久化一个事件(一个 JSON 串)即可,保证了高性能,无须依赖事务,而且通过 ENode 可以解决并发问题)。
我们只要保存了聚合根每次变化的事件(事件的结构怎么设计,本文不做多的介绍了,大家可以思考下),就相当于保存了聚合根的最新状态。而正是由于 Event Sourcing 技术的引入,让我们的模型可以一直存活在内存中,即可以使用 in-memory 技术。不要小看 in-memory 技术,in-memory 技术在某些方面对提高命令的处理性能非常有帮助。
比如就以我们车次聚合根处理出票的逻辑,假设某个车次有大量的命令发送到分布式消息队列,然后有一台机器订阅了这个队列的消息,然后这台机器处理这个车次的订票命令时,由于这个车次聚合根一直在内存,所以就省去了每次要去数据库取出聚合根的步骤,相当于少了一次数据库 IO。
这样的好处是,因为一个车次能够真正出售的票是有限的,因为座位就那么几个,比如就 1000 个座位,估计一般正常情况也就出个 2000 个左右的票吧(具体能出多少张票要取决于区间的相交程度,上面分析过)。也就是说,这个聚合根只会产生 2000 个事件,也就是说只会有 2000 个订票命令的处理是会产生事件,并持久化事件;而其余的大量命令,因为车次在内存计算后发现没有余票了,就不会做任何修改,也不会产生领域事件,这样就可以直接处理下一个订票命令了。这样就可以大大提高处理订票命令的性能。
另外一个问题我觉得还需要提一下,因为用户订票成功后,还需要付款。但用户有可能不去付款或者没有在规定的时间内完成付款。那这种情况下,系统会自动释放该用户之前订购的票。所以基于这样的需求,我们在业务上需要支持业务级别的 2pc。即先预扣库存,也就是先占住这张票一定时间(比如 15 分钟),然后付款成功后再真实给你这张票,系统做真正的库存修改。
通过这样的预扣处理,可以保证不会出现超卖的情况。这个思路其实和传统电商比如淘宝这样的系统类似,我就不多展开了,我之前写的 Conference 案例也是这样的思路,大家有兴趣的可以去看一下我之前录制的视频。
2、查询余票的实现思路
我觉得余票的查询的实现相对简单。虽然对于 12306 来说,查询的请求占了 80%,提交订单的请求只占 20%。但查询由于对数据没有修改,所以我们完全可以使用分布式缓存来实现。我们只需要精心设计好缓存的 key 即可;缓存 key 的多少要看成本,如果所有可能的查询都设计对应的 key,那时间复杂度为 1,查询性能自然高;但代价也大,因为 key 多了。如果想 key 少一点,那查询的复杂度自然要上去一点。所以缓存设计无非就是空间换时间的思路。然后,缓存的更新无非就是:自动失效、定时更新、主动通知 3 种。通过 CQRS 架构,由于 CQ 两端是事件驱动的,当 C 端有任何状态变化,都会产生对应的事件去通知 Q 端,所以我们几乎可以做到 Q 端的准实时更新。
同时由于 CQ 两端的完全解耦,Q 端我们可以设计多种存储,如数据库和缓存(Redis 等);数据库用于线下维护关系型数据,缓存用户实时查询。数据库和缓存的更新速度相互不受影响,因为是并行的。对同一个事件,可以 10 台机器负责更新缓存,100 台机器负责更新数据库。即便数据库的更新很慢,也不会影响缓存的更新进度。这就是 CQRS 架构的好处,CQ 的架构完全不同,且我们随时可以重建一种新的 Q 端存储。不知道大家体会到了没有?
关于缓存 key 的设计,我觉得主要从查询余票时传递的信息来考虑。12306 的关键查询是:出发地、目的地、出发日期三个信息。我觉得有两种 key 的设计思路:
直接设计了该查询条件的 key,然后快速拿到车次信息,直接返回;这种方式就是要求我们系统已经枚举了所有车次的所有可能出现的票(区间)的缓存 key,相信你一定知道这样的 key 是非常多的。
不是枚举所有区间,而是把每个车次的每个原子区间(相邻的两个站点所连成的直线)的可用票数作为 key。这样,key 就非常少了,因为车次假如有 10000 个,然后每个车次平均 15 个区间,那也就 15W 个 key 而已。当我们要查询时,只需要把用户输入的出发地和目的地之间的所有原子区间的可用票数都查出来,然后比较出最小可用票数的那个原子区间。则这个原子区间的可用票数就是用户输入的区间的可用票数了。当然,到这里我提到考虑出发日期。我认为出发日期是用来决定具体是哪个车次聚合根的。同一个车次,不同的日期,对应的聚合根实例是不同的,即便是同一天,也可能有多个车次聚合根,因为有些车次一天有几班的,比如上午 9 点发车的一班,下午 3 点发车的一般。所以,我们也只要把日期也作为缓存 key 的一部分即可。
总结
本文完全是凭自己对 12306 这个网站的核心业务的简单思考而得到的一些设计结果。如果真正的 DDD 领域建模,更多的是要和业务一线的工作人员、领域专家进行深入沟通,才能更深入的了解该领域内的业务知识,从而才能设计出更靠谱的领域模型和架构设计。
非常惭愧,我没有上 12306 买过火车票,家离的比较近,就算要买也是家人给我买:)所以,本文所分享的内容难免是纸上谈兵。但我觉得 12306 这个系统的业务确实比传统的电商系统要复杂,且并发又这么高。所以,我觉得这个系统真的很值得大家重视模型的设计,而不只是只关注技术层面的实现。
以上就是关于数据结构 航空订票系统 这个我是根据网上给的程序修改了一下下。。。不太成功。。求解全部的内容,包括:数据结构 航空订票系统 这个我是根据网上给的程序修改了一下下。。。不太成功。。求解、常见的数据库有哪些、不就是一个订票网站吗 12306 的核心模型设计思路究竟复杂在哪里等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)