阿里面试题——天猫部

简介: 设计12306售票系统的数据库订票系统分解,得到位池、余票池、订票池、取票池四个票务处理系统,以及独立的前台伺服系统、支付系统和通知系统,其中的精华就是位池、余票池系统。

设计12306售票系统的数据库

订票系统分解,得到位池、余票池、订票池、取票池四个票务处理系统,以及独立的前台伺服系统、支付系统和通知系统,其中的精华就是位池、余票池系统。售票窗和电话取票内部原理与网上订票一样,只是多了一个人工客服或电话客服。将订票系统分解,目的是使订票事件异步处理,独立系统处理专业事件,实现简单,效率更高。子系统对应的数据结构也更符合实际需求,数据处理更快。每个子系统的处理任务不同,对服务器的要求也不同,从而实现服务器的灵活配置。分解系统的依据是使票务信息数据库结构更简单、查询链更短、“静态化”。如此以来,我们查询票务信息时面对的仿佛就是一张简单的表格,根据我们提供的列车车次或者出发站/目的站直接就可以定位到相应列车的余票信息。而且这个余票信息还是实时更新的——后台驱动在默默地为您服务。

位池系统

 位池系统是余票查询与订票的基础。位池数据库记录所有列车每一个票位在沿途各站点的使用情况
{ /** 位池文档结构,订票系统的根基,发生订票时,向余票池请求减去或增加相应票位,向待支付票池请求生成订单。
一个文档对应列车中的一个位,如座位或者卧铺位,甚至站位。
文档的集合形成一个特定车次的列车。
一般一趟车次有不到2000车位,即集合里面文档树不到2000个,如开放站票可能会多点。
一天内所有列车集合形成当日所有列车位池数据库,当日列车位池形成当日位池数据库。
全国有不到5000次列车,即表示每日的数据库中约有不到5000个集合,每日一个数据库*/
    "_id" : 列车位唯一ID,    //由‘日期+列车ID+列车位ID’编码而成。
    "seat_type" : 位类型,    //如上铺、中铺、下铺、座位、站票、一等座、二等座等,该字段建索引。
    "station" : {    //途经站点,标记该位车票在哪一个站点被谁订购,下面以G1011武汉-深圳北(高铁)站点数据示例。
        "武汉" : null,    //null表示该位在该站点无人订购。
        "咸宁北" : null,
        "岳阳东" : 410xxxxx10,    //表示身份证(或其它证件号)为410xxxxx10的顾客订购了岳阳东到株洲西的票。
        "长沙南" : 410xxxxx10,
        "株洲西" : 430xxxxx30,    //表示身份证(或其它证件号)为430xxxxx30的顾客订购了郴州西到深圳北的票。
        "郴州西" : 430xxxxx30,
        "广州南" : 430xxxxx30,
        "虎门" : 430xxxxx30,
        "深圳北" : null
    }
}

位池驱动程序专门伺服位池数据库,功能如下:

根据票务字典,每日出票阶段进行数据初始化;
接受来自订票池的请求。对于下单事件所有列车票请求(一个订单有一张或多张列车票),在位池数据库中查找特定列车车次的某个符合位类型的列车位文档,检查相应站段是否为null。如果所有要求的列车票均存在,则在相应请求站段写入顾客证件号。写入成功后向余票池发出请求,要求余票池减去相应票位ID,同时向订票池反馈下单成功。
接受来自订票池的退订请求和来自取票池的退票请求,根据请求,把位池中相应的车位重置为null,请求余票池增加相应的票位ID,同时向订票池或取票池反馈请求成功。

订票池

订票池系统以最简约的方式受理订票事件。支持同时订多张票和备选订票方案

{ /** 订票池文档结构,处理过期未支付的无效订单,为支付系统提供数据。
一个订单文档对应一个下单人的下单事件。*/
    "_id" : 订单唯一ID,    //由‘下单人编号+下单时间+订单编号’编码而成。
    "gen_time" : null,    //下单时为null,下单成功后填入订单生成时间值,也标记了有效支付期的开始。
    "pay_time" : null,    //订单支付时间,驱动程序扫描该字段,null表示未支付,存在时间值表示已支付或者已被锁定请求释放中。
    "pay_info" : {订单支付详细信息},    //支付信息对象,初始值为null,支付成功后即写入数据。
    "order" : [{    //主订单列车票对象数组,数组中每个对象代表一张预定的列车票信息
        "seat_type" : 位类型,    //如上铺、中铺、下铺、座位、站票、一等座、二等座等,该字段建索引。
        "seat_num" : null,    //即位池中的列车位唯一ID,下单时为null,下单成功后填入位ID。
        "price" : 票价,    //根据订票站段信息从字典表查询而来。
        "customer" : {    //订票人信息
            "name" : 姓名,
            "cer" : 证件,
            "cer_num" : 证件号,
            "phone" : 手机号,
            "email" : 电子邮箱
        },
        "s_station" : 出发站名称,    //出发站点,如上面430xxxxx30顾客,该值为“株洲西”。
        "d_station" : 到达站名称,    //到达站点,如上面430xxxxx30顾客,该值为“深圳北”。
        },
        {第二张列车票},    //订单中第二张列车票对象。
        {...}],     //更多。
    "re_order" : [{    //备用订单列车票对象数组,结构同上,主订单失败时启用备用订单。
        "seat_type" : 位类型,    //如上铺、中铺、下铺、座位、站票、一等座、二等座等,该字段建索引。
        "seat_num" : null,    //即位池中的列车位唯一ID,下单时为null,下单成功后填入位ID。
        "price" : 票价,    //根据订票站段信息从字典表查询而来。
        "customer" : {    //订票人信息
            "name" : 姓名,
            "cer" : 证件,
            "cer_num" : 证件号,
            "phone" : 手机号,
            "email" : 电子邮箱
        },
        "s_station" : 出发站名称,    //出发站点,如上面430xxxxx30顾客,该值为“株洲西”。
        "d_station" : 到达站名称,    //到达站点,如上面430xxxxx30顾客,该值为“深圳北”。
        },
        {第二张列车票},    //订单中第二张列车票对象。
        {...}]     //更多。
}

订票池驱动程序专门伺服订票池数据库,功能如下:

接受顾客订单请求,生成订单文档。然后向位池系统发出订单请求,位池系统反馈订单成功后向订单文档每个列车票对象写入成功获得的列车位ID和订单正时生成时间。然后向顾客反馈下单成功,同时向支付系统发出支付请求。如果下单失败,启用备用订单,再次向位池系统发出订单请求。如果备用订单仍失败,则删除该订票文档,同时向顾客反馈下单失败。
若支付系统反馈支付成功,则向取票池发出请求生成取票文档,同时在订票池中删除该已支付的订票文档。
对于超过有效支付期的订票文档,向位池系统发出退单请求,位池系统反馈退单成功后即删除该订票文档。

取票池

取票池系统生成取票文档,受理查询、退票、出票、记录存档等事件。

{ /** 取票池文档结构,处理退票、出票事件。与订票池文档结构不同,一个取票池文档对应一张列车票。
由于取票操作是以证件号为依据,需对[customer][cer_num]字段建索引。
根据出发站点将取票文档分成不同集合不同数据库,提高查询效率。
按照前面假设的数据最大化估算,取票池总文档数量可能达到亿级。*/
    "_id" : 列车票唯一ID,    //由‘列车位唯一ID+订票人证件类型+订票人证件号’编码而成,记录至订票系统个人账户中。
    "type" : 位类型,    //如上铺、中铺、下铺、座位、站票、一等座、二等座等。
    "pay_time" : 支付时间,    //订单支付时间,也是车票确认时间。
    "pay_interface" : 订单支付接口,    //订单支付接口。
    "pay_info" : {订单支付详细信息},    //支付信息对象,为退票提供退款目标。
    "ticket_time" : 出票时间,    //null表示未取票。
    "ticket_place" : 出票地点,    //null表示未取票。
    "price" : 票价,    //根据订票信息从字典表查询而来。
    "seat" : 位置编号,    //如“XX车厢XX号上铺”,根据“_id”中列车位唯一ID,查询票务字典得到。
    "customer" : {    //订票人信息
        "name" : 姓名,
        "cer" : 证件类型,
        "cer_num" : 证件号,    //该字段建索引。
        "phone" : 手机号,
        "email" : 电子邮箱
    },
    "s_station" : 出发站名称,
    "d_station" : 到达站名称,
    "s_time" : 出发时间,    //根据订票信息查询票务字典得到,人性化。
    "d_time" : 到达时间,    //根据订票信息查询票务字典得到,人性化。
}

取票池驱动程序专门伺服取票池数据库,功能如下:

根据订票池的请求生成相应的取票文档,若一个订票文档包含多张列车票,则会生成多个取票文档,同时将取票文档记录到用户账号,方便用户查询或退票;
若用户发出某张列车票退票请求,向位池发出退票请求;
获得退票请求成功反馈后向支付系统请求退还相应款项;
获得退款成功反馈后将该退票文档永久存档,同时在取票池中删除相应的取票文档。
响应现场取票机的查询及取票请求,取票成功后向该文档写入取票时间、地点,然后将该取票文档永久存档,同时在取票池中删除相应的取票文档。
对于超过取票期限的取票文档,执行退票请求

余票池

余票池系统是订票系统的关键,对用户而言,余票池系统实现了伪静态化特征。它还能体现某一列车位在不同站点空置情况。

{ /** 余票池文档结构,处理余票查询事件。
位池发生订票行为时,向余票池发出信息,余票池根据订票日期、列车ID、列车位ID,出发站和到达站信息,在余票池中删除相应的余票信息。
一个文档对应一趟列车的余票信息,所有列车当日余票信息组成一个集合,订票期内(如12天)每天所有列车集合共形成一个余票池数据库。
针对春节的返程票订购,可另外生成一个返程票余票池数据库。*/
    "_id" : 列车唯一ID,    //由‘日期+列车ID’编码而成。以下字段属性名为该列车所经站点,对应值是可能目的站点的组合对象。以G1011为例。
    "武汉" : {    //出发车站
        "咸宁北" : {    //表示武汉到咸宁北有以下类型车票及相应列车位ID数组。
            "位类型1" : [列车位唯一ID,,],    //表示位类型1,如一等座,剩余列车位ID数组。数组长度即剩余数量。
            "位类型2" : [列车位唯一ID,,],    //如,二等座。。。
            ...    //其它位类型,如中铺、上铺等,省略表示。
        },
        "岳阳东" : {,,},    //武汉到岳阳东段的余票,省略表示,下同。
        "长沙南" : {,,},
        "株洲西" : {,,},
        "郴州西" : {,,},
        "广州南" : {,,},
        "虎门" : {,,},
        "深圳北" : {,,}
    },
    "咸宁北" : {    //出发车站
        "岳阳东" : {,,},
        "长沙南" : {,,},
        "株洲西" : {,,},
        "郴州西" : {,,},
        "广州南" : {,,},
        "虎门" : {,,},
        "深圳北" : {,,}
    },
    "岳阳东" : {
        "长沙南" : {,,},
        "株洲西" : {,,},
        "郴州西" : {,,},
        "广州南" : {,,},
        "虎门" : {,,},
        "深圳北" : {,,}
    },
    "长沙南" : ...
    "株洲西" : ...
    "郴州西" : ...
    "广州南" : ...
    "虎门" : {
        "深圳北" : {
        "位类型1" : [列车位唯一ID,,],
        "位类型2" : [列车位唯一ID,,],
        ...
        }
    },
    "深圳北" : null
}

余票池驱动程序专门伺服余票池数据库,功能如下:

根据票务字典对余票池初始化,未出票时,余票池包含所有的列车位ID。
响应位池的请求,增加或减去指定列车位ID。以G1011为例,假设430xxxxx30顾客订购郴州西到深圳北的一等座。位池处理后得到了该车票的列车位唯一ID,假设是“20121001-G1011-0117”号(2012年10月1日G1011次1号车厢17座),向余票池发出减票请求。余票池收到请求,找到该趟G1011次列车的余票文档。从始发站字段“武汉”开始,在其中郴州西到深圳北的“一等座”属性对应的数组中,删除“20121001-G1011-0117”,然后依次在“咸宁北”、“岳阳东”、“长沙南”…“虎门”做类似操作。其中,深圳北是到达站,订票时位池系统并未占位写入顾客身份证,这里也无需做相应删除操作。诸位也许觉得,操作有点多~,但这是必须的,为了最短的查询路径。查询事件总是远大于订票事件的。
处理查询请求。例如,410xxxxx10顾客查询10月1日从岳阳东到株洲西的票。余票池驱动先根据票务字典查找出10月1日所有从岳阳东和株洲西的列车ID,再根据列车ID查询对应的余票文档,在余票文档中找到主键“岳阳东”,再在其对象中找到目的地“株洲西”,最后返回[岳阳东][株洲西]对应的对象值:位类型和列车位ID数组长度的键值对。今后若提供选座功能,则该查询列车位ID数组。这应该是最短查询路径。

票务字典(静态数据库)

列车趟次——途径站名——到达/出发时间——票价字典

{    /*列车趟次——途径站名——到达/出发时间——票价字典,静态值。
    与下面的车站名——列车趟次字典等组成票务字典数据库。为位池、票池、余票池等提供基础信息。*/
    "_id" : 列车ID,    //由‘车次’编码而成。以下字段属性名为该列车所经站点,对应值是可能目的站点的组合对象。以G1011为例。
    "武汉" : [
        到达时间,    //始发站为null
        发车时间,    //发车时间
        {
        "咸宁北" : {    //表示武汉到咸宁北有以下类型车票及票价。
            "位类型1" : 票价,    //表示位类型1,如一等座,及对应票价。
            "位类型2" : 票价,
            ...    //其它位类型,如中铺、上铺等,省略表示。
            },
        "岳阳东" : {,,},
        "长沙南" : {,,},
        "株洲西" : {,,},
        "郴州西" : {,,},
        "广州南" : {,,},
        "虎门" : {,,},
        "深圳北" : {,,}
        }
    ],
    "咸宁北" : [
        到达时间,    //到达咸宁北的时间
        发车时间,    //发车时间
        {
        "岳阳东" : {,,},
        "长沙南" : {,,},
        "株洲西" : {,,},
        "郴州西" : {,,},
        "广州南" : {,,},
        "虎门" : {,,},
        "深圳北" : {,,}
        }
    ],
    "岳阳东" : [
        到达时间,    //到达岳阳东的时间
        发车时间,    //发车时间
        {
        "长沙南" : {,,},
        "株洲西" : {,,},
        "郴州西" : {,,},
        "广州南" : {,,},
        "虎门" : {,,},
        "深圳北" : {,,}
    ],
    "长沙南" : [,,],
    "株洲西" : [,,],
    "郴州西" : [,,],
    "广州南" : [,,],
    "虎门" : [,,],
    "深圳北" : null
}
列车趟次——位类型——列车
列车趟次——位类型——列车位ID字典

{    /*列车趟次——位类型——列车位ID字典,静态值。*/
    "_id" : 列车ID,
    "位类型1" : [列车位ID数组],
    "位类型2" : [列车位ID数组],
    ...
}
###车站名——列车趟次字典

{    /*车站名——列车趟次字典,一般车票查询基于车站到车站.
该字典迅速提供车站到车站的可用列车车次,然后根据列车车次查询相关余票。*/
"武汉" : [靠站列车1唯一ID, 靠站列车2唯一ID,,,,],   //表示在武汉站停车的所有列车车次。
"株洲" : [,,,],
...    //全国所有列车站的数据字典。
}
目录
相关文章
|
7月前
|
Python 开发工具
2024年Python最全使用Python实现音频双通道分离,2024年最新阿里p7面试难度
2024年Python最全使用Python实现音频双通道分离,2024年最新阿里p7面试难度
2024年Python最全使用Python实现音频双通道分离,2024年最新阿里p7面试难度
|
2月前
|
存储 关系型数据库 MySQL
阿里面试:为什么要索引?什么是MySQL索引?底层结构是什么?
尼恩是一位资深架构师,他在自己的读者交流群中分享了关于MySQL索引的重要知识点。索引是帮助MySQL高效获取数据的数据结构,主要作用包括显著提升查询速度、降低磁盘I/O次数、优化排序与分组操作以及提升复杂查询的性能。MySQL支持多种索引类型,如主键索引、唯一索引、普通索引、全文索引和空间数据索引。索引的底层数据结构主要是B+树,它能够有效支持范围查询和顺序遍历,同时保持高效的插入、删除和查找性能。尼恩还强调了索引的优缺点,并提供了多个面试题及其解答,帮助读者在面试中脱颖而出。相关资料可在公众号【技术自由圈】获取。
|
4天前
|
存储 NoSQL 架构师
阿里面试:聊聊 CAP 定理?哪些中间件是AP?为什么?
本文深入探讨了分布式系统中的“不可能三角”——CAP定理,即一致性(C)、可用性(A)和分区容错性(P)三者无法兼得。通过实例分析了不同场景下如何权衡CAP,并介绍了几种典型分布式中间件的CAP策略,强调了理解CAP定理对于架构设计的重要性。
25 4
|
21天前
|
存储 NoSQL 算法
阿里面试:亿级 redis 排行榜,如何设计?
本文由40岁老架构师尼恩撰写,针对近期读者在一线互联网企业面试中遇到的高频面试题进行系统化梳理,如使用ZSET排序统计、亿级用户排行榜设计等。文章详细介绍了Redis的四大统计(基数统计、二值统计、排序统计、聚合统计)原理和应用场景,重点讲解了Redis有序集合(Sorted Set)的使用方法和命令,以及如何设计社交点赞系统和游戏玩家排行榜。此外,还探讨了超高并发下Redis热key分治原理、亿级用户排行榜的范围分片设计、Redis Cluster集群持久化方式等内容。文章最后提供了大量面试真题和解决方案,帮助读者提升技术实力,顺利通过面试。
|
25天前
|
SQL 关系型数据库 MySQL
阿里面试:1000万级大表, 如何 加索引?
45岁老架构师尼恩在其读者交流群中分享了如何在生产环境中给大表加索引的方法。文章详细介绍了两种索引构建方式:在线模式(Online DDL)和离线模式(Offline DDL),并深入探讨了 MySQL 5.6.7 之前的“影子策略”和 pt-online-schema-change 方案,以及 MySQL 5.6.7 之后的内部 Online DDL 特性。通过这些方法,可以有效地减少 DDL 操作对业务的影响,确保数据的一致性和完整性。尼恩还提供了大量面试题和解决方案,帮助读者在面试中充分展示技术实力。
|
2月前
|
消息中间件 存储 canal
阿里面试:canal+MQ,会有乱序的问题吗?
本文详细探讨了在阿里面试中常见的问题——“canal+MQ,会有乱序的问题吗?”以及如何保证RocketMQ消息有序。文章首先介绍了消息有序的基本概念,包括全局有序和局部有序,并分析了RocketMQ中实现消息有序的方法。接着,针对canal+MQ的场景,讨论了如何通过配置`canal.mq.partitionsNum`和`canal.mq.partitionHash`来保证数据同步的有序性。最后,提供了多个与MQ相关的面试题及解决方案,帮助读者更好地准备面试,提升技术水平。
阿里面试:canal+MQ,会有乱序的问题吗?
|
7月前
|
机器学习/深度学习 Python 算法
最新【Python 百练成钢】时间调整、二进制数、回文素数、字母距离(1),2024年最新2024年阿里Python岗面试必问
最新【Python 百练成钢】时间调整、二进制数、回文素数、字母距离(1),2024年最新2024年阿里Python岗面试必问
最新【Python 百练成钢】时间调整、二进制数、回文素数、字母距离(1),2024年最新2024年阿里Python岗面试必问
|
2月前
|
消息中间件 架构师 Java
阿里面试:秒杀的分布式事务, 是如何设计的?
在40岁老架构师尼恩的读者交流群中,近期有小伙伴在面试阿里、滴滴、极兔等一线互联网企业时,遇到了许多关于分布式事务的重要面试题。为了帮助大家更好地应对这些面试题,尼恩进行了系统化的梳理,详细介绍了Seata和RocketMQ事务消息的结合,以及如何实现强弱结合型事务。文章还提供了分布式事务的标准面试答案,并推荐了《尼恩Java面试宝典PDF》等资源,帮助大家在面试中脱颖而出。
|
2月前
|
SQL 关系型数据库 MySQL
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
尼恩,一位40岁的资深架构师,通过其丰富的经验和深厚的技術功底,为众多读者提供了宝贵的面试指导和技术分享。在他的读者交流群中,许多小伙伴获得了来自一线互联网企业的面试机会,并成功应对了诸如事务ACID特性实现、MVCC等相关面试题。尼恩特别整理了这些常见面试题的系统化解答,形成了《MVCC 学习圣经:一次穿透MYSQL MVCC》PDF文档,旨在帮助大家在面试中展示出扎实的技术功底,提高面试成功率。此外,他还编写了《尼恩Java面试宝典》等资料,涵盖了大量面试题和答案,帮助读者全面提升技术面试的表现。这些资料不仅内容详实,而且持续更新,是求职者备战技术面试的宝贵资源。
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
|
2月前
|
Kubernetes 架构师 算法
阿里面试:全国14亿人,统计出重名最多的前100个姓名
文章介绍了如何解决“从全国14亿人的数据中统计出重名人数最多的前100位姓名”的面试题,详细分析了多种数据结构的优缺点,最终推荐使用前缀树(Trie)+小顶堆的组合。文章还提供了具体的Java代码实现,并讨论了在内存受限情况下的解决方案,强调了TOP N问题的典型解题思路。最后,鼓励读者通过系统化学习《尼恩Java面试宝典》提升面试技巧。
阿里面试:全国14亿人,统计出重名最多的前100个姓名

热门文章

最新文章