售票系统卖票问题总结

本贴最后更新于 2108 天前,其中的信息可能已经东海扬尘

这篇文章总结一下最近朋友公司遇到的售票系统上线以及卖票的问题。

这是个售票系统,卖明星的演唱会门票。这种门票的售卖场景其实跟秒杀是类似的,都是集中在某一个时间段内并发订单量会非常很高。年前卖票一共 4 场,每场订单量在 1w 以内。接下来还原每一场的问题。

一、第 1 场

1.1 订单保存接口并发慢

在上线之前,对售票系统进行压测,发现订单保存接口很慢,差不多 200 并发就会存在异常响应。扣减库存过程用的是 redis 分布式锁,分布锁本身存在羊群效应,每次只有一个人能过,并发肯定上不去,所以在高并发下场景下,效果不好,某些订单会超时,超时也带来另一个问题,用户可能已经退出来再次尝试发起订单了,会增加系统负载。

由于时间紧急,来不及优化,考虑到订单不多,硬着头皮上线了。这也考虑到了即便部分用户失败也是可以接受的,只要票成功卖掉就行了。好在上线的时候,数据库的问题盖过了该问题,订单保存的接口没出问题。

1.2 数据库满负荷

出问题的是数据库,由于监控没有跟上,1 个小时之后才发现数据库 CPU 一直 100%。由于有测试环境的压测,压根没有想到数据库会存在瓶颈。好在用的是云服务,发现后直接扩容解决了该问题,数据库的负载下来了。回顾了下原因,主要是线上库的数据量比测试环境大多了,压测时没有考虑这一点。当然还有一些其它问题,比如索引也没有用好,字段设计存在很多不合理的地方,字段太长等等。

1.3 超卖

这一场存在超卖的情况,大致超了几十张票的样子,分散到每个票档,不多。上线时对这个问题比较重视,预留了库存,业务上解决了这个问题。技术并没有找到问题在哪里,因为测试压测过,没有问题,并且用了分布式锁,理论是解决了库存问题的。不过,由于订单保存接口要优化,就没有继续调查下去。

二、第 2 场

第 2 场,我们对订单保存接口做了调整。主要测试了两个版本:

  1. 对 redis 锁做优化,尝试活锁检测
    结果测试下来,并发上并没有提升,因为最终还是一群“羊”一个个过“栅栏门”。
  2. 使用 redis 队列来实现库存扣减
    测试环境测试了下,并发数上去了很多,将近 1000 的并发,没有报错,性能提升了将近 10 倍,在测试环境没有超卖的情况。目前是最优的方案了。

2.1 答题列表缓存不对

在下单之前,有一个答题模块,为阻止黄牛的模块。在上线前,为了能够进行压测,把题库改成了测试题库。结果上线之后,题库还是测试题库。影响了正常购票。发现后,我们就去 redis 里删除题库,但是一直删除不了,还是旧题库。这里要吐槽下,Redis Desktop Manager 不知道为什么删除不了,去命令行下 redis-cli 执行 del 就成功了,关键时刻还是官方命令行靠谱。

2.2 超卖

这场还是超卖了,而且比较严重。这并不是 redis 队列的错,而是上线时失误了,一个订单可以卖两张票,结果上线的时候,代码的逻辑还是一张的。
这场实在协调不了票,只有退款了。公司的客服面临了很大的压力,很多明星的粉丝是哭着打电话过来的。真的心酸无助,做技术的切记:仔细仔细再仔细,否则一个人几天的心情说不定就被你毁了,跟拼多多优惠劵毁了一个部门的年终奖是一个道理。

二、第 3 场

第 3 场主要是把程序完善了。但是还是遇到了超卖问题,不过找到了前几场一直存在的问题。

3.1 超卖

这次分批次手工放票卖了,最终只超卖了一张,但是在放票过程中,其实一直有超卖的情况,而且不少。但是不能理解的是,已经用了 redis 队列,仔细核对订单保存代码,也没有发现问题。测试压测也没有问题。到底问题出现在哪儿呢?

通过分析数据,最终发现,是另一个支付环节出了问题:

系统配置的订单超时是 5 分钟,但支付超时却设置成了 30 分钟。

有将近 50 笔订单在超时 5 分钟的时候被标记为过期并回滚库存了。而在支付成功后,系统没有做判断直接将订单状态又改成了支付成功。这些订单不在库存限制之中,所以出现了超卖。

需要做以下优化:

  1. 支付超时与订单超时设成一致。
  2. 超时时间设置得更长一些,减少因为支付过长导致的问题,比如 10 分钟。
  3. 支付成功时,判断当前订单状态是否已经变成过期,如果过期,则标记为需退款状态,由人工进行退款处理。

其中第 3 点在业务上不被允许的,因为付了钱就是合同成立了,单方面退款算违约。但是我觉得还是需要有个日志或是通知机制来保底,不信任第三方的判断是需要的。

四、第 4 场

// TODO
但愿没问题

总结

  1. 分布式锁尽量在低并发的情况下用,比如更新过期订单这种。高并发下分布式锁并不是一个好的解决方案,会存在性能瓶颈。

  2. 数据库设计要慎重,字段的长度一定要限定好,用索引的时候,字段类型一定要匹配。

  3. 代码里的日志对各种异常情况一定要详细记录,方便回顾定位问题。日志会对性能有影响,但几乎不会产生决定性的影响。

  4. 理解业务远比理解技术重要,技术架构也是在明确了业务之后才能确定的。不清楚,就动手画画图,对理解特别有帮助。

  • 秒杀
    8 引用 • 3 回帖 • 1 关注
  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    286 引用 • 248 回帖 • 74 关注
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    337 引用 • 705 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...