`
ahyyxx222
  • 浏览: 3698 次
  • 性别: Icon_minigender_1
  • 来自: 玉林
文章分类
社区版块
存档分类

参考了LMAX架构思想的自创12306订票的极限速度方案,无需动用事务和数据库锁

阅读更多

我的方案将实现一个订单全程只进行一次insert操作,查余票无需发送select语句,完全消除由于事务、库表锁行锁带来的性能损耗。

 

流程:

1、将一趟车的所有票况在系统启动时存入一个该趟车专有类中,票况信息为单例。类中有4个静态的余票数组(站票、坐票、硬卧、软卧),存储该趟车各途经站点下完乘客后的剩余的座位数(余票),用的是数组而不是整数是因为现在的列车座位是会被复用的。

2、类中有两个主要方法,一个查余票,一个查余票并购票,查余票方法仅从本类的余票数组中获取余票数,购票方法在查了票以后若有余票则扣除,无论有无余票,都会发出一条insert,保存订单号、车次、乘车日期、起始站、终点站、票的种类、购票结果(成功/失败)。

3、要买该趟车的订单被排入4个队列,分别对应站票、坐票、硬卧、软卧队列。

4、每个队列挨个取订单来调用购票方法,由于操作的是对应的4个不同余票数组,所以不会出现并发操作。

5、用户请求在被排入队列时就已返回,用户持有订单号,将在若干秒后通过ajax查订单处理结果。查结果的方式为,往之前insert的表中按订单号查结果是成功还是失败(订单号为索引)。若无则判为仍在队列中,提示等待,一定时间后ajax再来。

 

分析下性能:

由于未动用数据库锁,购票过程就是数组值操作加一条insert,所以可以非常快,相信在调优下购票业务逻辑单机对某趟车一秒钟可以处理2000订单以上,实现各趟车分库后应该还能大大提高。一趟车大约有10000张票,若同时有10万人在秒杀该趟车次,则50秒可以全部告知用户是否秒杀成功,然后用户视结果换一种票类或换一趟车继续秒杀。

当然全国一天有近4000趟车,全都这么极端实现就得去租4000台服务器了,一台装一个数据库,一个库就为一趟车服务。就算一台100元租一天,一天要花40万租服务器,那可以考虑适当整合一下,几趟车用一台数据库服务器,性能也仍可接受,毕竟高峰期持续不长。

 

再提下最重要的宕机处理:

既然实时票数存在单例的类中,那就要应付可能出现的机器挂掉的情况。借鉴事件驱动思想,我的方案是:换一台业务服务器顶上,把挂掉的各趟车的类重新初始化为初始总票数,然后从库里查出所有的已成功订单来挨个算票,重新在数组里减一遍票,就能恢复到宕机前的余票情况了。一天最多数百万成功订单,全查出来再算一遍,并不需要很长时间,宕机这情况本来就少见,实在遇上了花半小时再重算下票数不过份,何况怎么可能所有算票服务器全挂了呢。

 

 

算票业务类粗略实现如下,仅供参考,未细化验证:

import java.util.HashMap;
import java.util.Map;

// 该趟车为上海始发,途经苏州、南京、天津、终点站北京
public class TicketTest {
 // 用来保存购票结果的dao,库表结构为 订单id,起始站,到达站,票的类别,购票结果
 private TicketDao ticketDaoImpl;

 // 该趟车各类票的总数
 private final static int zhanpiao = 2000;
 private final static int zuopiao = 2000;
 private final static int yingwo = 1000;
 private final static int ruanwo = 200;
 // 该趟车从始发站开至各个站点的余票(座)
 private final static int[] zhanpiaoArr = new int[] { zhanpiao, zhanpiao,
   zhanpiao, zhanpiao };
 private final static int[] zhuopiaoArr = new int[] { zuopiao, zuopiao,
   zuopiao, zuopiao };
 private final static int[] yingwoArr = new int[] { yingwo, yingwo, yingwo,
   yingwo };
 private final static int[] ruanwoArr = new int[] { ruanwo, ruanwo, ruanwo,
   ruanwo };
 private final static Map<String, Integer> zhanming = new HashMap<String, Integer>();

 public void init() {
  // 始发站上海不用存,数组中也没有上海的余票,因为不存在从上海坐到上海这种情况
  // value值,为到达该城市余票在票数数组(如zhanpiaoArr)中的下标
  zhanming.put("苏州", 0);
  zhanming.put("南京", 1);
  zhanming.put("天津", 2);
  zhanming.put("北京", 3);
 }

 // 获取从start站到end站的余票,type为票的种类:站、坐、硬卧、软卧
 public int getRemainingTicket(String start, String end, int type) {
  int startIndex = zhanming.get(start);
  int endIndex = zhanming.get(end);
  int remainingNumber = 0;
  int[] array = new int[zhanming.size()];
  // 确定从哪类余票数组中查票,并把余票初始化为该类票最大票数
  switch (type) {
  case 1:
   array = zhanpiaoArr;
   remainingNumber = zhanpiao;
   break;
  case 2:
   array = zhuopiaoArr;
   remainingNumber = zuopiao;
   break;
  case 3:
   array = yingwoArr;
   remainingNumber = yingwo;
   break;
  case 4:
   array = ruanwoArr;
   remainingNumber = ruanwo;
   break;
  }

  // 查询从某站到某站每一站的剩余票数,以票最少的站点所剩票数为实际余票数
  // 例如卖掉了一张上海到苏州硬座,那苏州余票为1999,票数数组是[1999, 2000, 2000, 2000]
  // 此时若你要买上海到南京,则取途经站最小值,实际上只剩1999个座位
  // 而如果你想买的是苏州到北京,取途经站最小值则是剩2000张票,因为前面那张票的人在苏州下车了
  for (int i = startIndex; i <= endIndex; i++) {
   // 若找到最小值,则做为余票数
   if (array[i] < remainingNumber) {
    remainingNumber = array[i];
   }
  }

  return remainingNumber;
 }

 // 进行实时查票并购票,有4个订单队列在排队,每个队列都会逐个订单来调用此方法,因此同一种票,即同一余票数组不会被并发操作。
 public void buyTicket(String start, String end, int type, String orderId) {
  int remainingNumber = getRemainingTicket(start, end, type);
  if (remainingNumber > 0) {
   // 如果有余票,将要买的票扣除并将购买成功结果存库
   int[] array = new int[zhanming.size()];
   switch (type) {
   case 1:
    array = zhanpiaoArr;
    break;
   case 2:
    array = zhuopiaoArr;
    break;
   case 3:
    array = yingwoArr;
    break;
   case 4:
    array = ruanwoArr;
    break;
   }
   for (int i = array[zhanming.get(start)]; i < array[zhanming.get(end)]; i++) {
    // 从票数数组找到所有途经站点将票数减1,到达站不用减,因为你已下车,不会占用到达站的座位
    array[i] -= 1;
   }
   ticketDaoImpl.save(orderId, start, end, type, true);
  } else {
   // 无余票,直接将购买失败记录存库,这个不可省略,否则用户查询购票结果时将分不清是没买上还是仍在排队
   ticketDaoImpl.save(orderId, start, end, type, false);
  }
 }

 // set/get省略
 }

 

2
1
分享到:
评论
2 楼 jxauwxj 2015-06-30  
1 楼 jaiiye 2012-11-20  
很好!看了很多,都大谈架构,没有你汲及这么细的业务。

相关推荐

    Lmax品种明细表

    在提供的文件内容中,我们看到了一个关于金融产品的明细表,该表涵盖了LMAX交易公司的多种交易品种的详细资料。这些交易品种包括各种指数期货合约,以及对应的产品杠杆、合约大小、手续费、最小交易量、最大交易量、...

    LMAXCollections, LMAX集合.zip

    LMAXCollections, LMAX集合 LMAX集合高性能集合库维护者合并环缓冲:[Nick Zeeb] ( https://github.com/nickzeeb )是什么?请参见 http://nickzeeb.wordpress.com/2013/03/07/t

    Lmax-Net-Client-Library-1.8.4.0.zip_C#_lmax .net

    LMAX Net Client Library 1.8.4.0 是一个专为C#开发者设计的.NET库,用于与LMAX交易平台进行...通过充分利用.NET Framework的异步特性,开发者可以构建出高度响应和可靠的系统,满足金融领域对速度和准确性的严格要求。

    LMAX-Disruptor框架jar包

    Disruptor框架是由LMAX公司开发的一款高效的无锁内存队列。使用无锁的方式实现了一个环形队列。据官方描述,其性能要比BlockingQueue至少高一个数量级。根据GitHub上的最新版本源码打出的包,希望对大家有帮助。

    最新lmax在线开户注册指南收集.pdf

    最新lmax在线开户注册指南收集.pdf

    lmax.rar_Lmax_The Signal

    标题 "lmax.rar_Lmax_The Signal" 暗示我们关注的是一个与寻找数据中的局部最大值(Local Maxima)以及滤除高频信号相关的MATLAB程序,具体来说,是名为 "lmax.m" 的脚本文件。在这个场景中,"lmax" 可能是一个函数...

    LMAX disruptor jar包+Demo+Api+src源码 disruptor-3.0.1.jar

    综上所述,LMAX Disruptor是一个强大的并发工具,通过其独特的设计和优化,为高并发场景提供了高效的解决方案。了解和掌握Disruptor,能够帮助开发者构建更高效、更稳定的系统。通过提供的API、源码和示例,开发者...

    LMAX.Disruptor,一个无锁高并发框架,中文文档

    LMAX是一种新型零售金融交 易平台,它能够以很低的延迟产生大量交易。这个系统是建立在JVM平台上,其核心是一个业务逻辑处理 器,它能够在一个线程里每秒处理6百万订单。业务逻辑处理器完全是运行在内存中,使用事件...

    新lmax在线开户注册指南分享.pdf

    新lmax在线开户注册指南分享.pdf

    LMAX Client-开源

    LMAX Client是一款开源的金融交易平台客户端,专为与LMAX Exchange的Web服务和API交互而设计。这款应用程序的核心特点在于其免费提供,并且源代码开放,允许开发者和交易者深入理解其工作原理,同时也能根据自己的...

    Disruptor:一种高性能的、在并发线程间数据交换领域用于替换有界限队列的方案.pdf

    这种性能提升使得Disruptor成为异步事件驱动架构的理想选择,LMAX公司已经在订单匹配引擎、实时风险管理和内存事务处理系统等多个关键项目中成功应用了Disruptor,实现了前所未有的性能水平。 Disruptor并非仅限于...

    LMAX Disruptor 到 Go 语言的移植 .zip

    LMAX Disruptor 到 Go 语言的移植。Disruptor 概述这是LMAX Disruptor到 Go 编程语言的...在 Go 中,通道 ( chan) 的当前实现对发送、接收和操作保持锁定len,无争用访问的最大速度约为每秒 2500 万条消息 — 与 Disru

    论文研究-基于事务依赖的错误隔离及修复技术.pdf

    本研究论文专注于提高数据库在恢复过程中的服务有效性,通过建立一种基于数据项分类标记的错误隔离模型,并提出了基于事务依赖关系的数据修复算法。这些方法有助于缩短错误修复的时间,从而提高数据库系统的整体可用...

    基于Spring Boot和LMAX Disruptor的高性能并发框架.zip

    基于Spring Boot和LMAX Disruptor的高性能并发框架 项目简介 本项目是一个基于Spring Boot和LMAX Disruptor框架的高性能并发框架,旨在提供高效的事件处理和消息传递机制。项目涵盖了并发编程的核心概念、无锁...

    Disruptor:一种高性能的、在并发线程间数据交换领域用于替换有界限队列的方案

    在LMAX公司的实际应用中,例如顺序匹配引擎、实时风险管理系统和内存事务处理系统,都基于Disruptor实现了卓越的性能提升,打破了行业标准。 Disruptor的主要特点包括: 1. **减少写入争用**:通过避免在生产者和...

    最高效的交易所撮合引擎,采用伦敦外汇交易所LMAX开源的Disruptor框架,用Hazelcast进行分布式内存存.zip

    Disruptor通过环形缓冲区(Ring Buffer)结构设计,摒弃了锁机制,利用了现代CPU缓存架构的优势,从而显著提高了数据在多个生产者和消费者之间的流转速度。事件处理器链的设计进一步确保了事件处理的顺序性和效率,...

Global site tag (gtag.js) - Google Analytics